Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What if I declare a function with empty parameter table, then pass arguments to it?

For example,

#include <stdio.h>

void foo();

int main(void)
{
        foo();
        foo(42);
        foo("a string", 'C', 1.0);
        return 0;
}

void foo()
{
        puts("foo() is called");
}

Output:

foo() is called
foo() is called
foo() is called

This code compiles well(without warnings using clang) and runs well. But I wonder what happens to the values passed to foo()? Are they pushed in to the stack or just get discarded?

Perhaps this question sounds useless, but it does make sense. For example, when I have int main(), rather than int main(void), and pass some command line arguments to it, will the behavior of main() be affected?

Also, when using <stdarg.h>, at least one named parameter is required before ... by ISO C. Is it possible that we can make use of such declarations as void foo() to pass from zero to infinite arguments to a function?

I noticed that void foo() is a "non-prototype declaration" and that void foo(void) is a "prototype declaration" just now. Is this somewhat relevant?


Clarification

Seems that this question is marked duplicate to What does an empty parameter list mean? [duplicate](Interestingly, that question is also duplicate...). In fact, I don't think my question have anything to do with that one. It focuses on "What void foo() means in C", but I know this means that "I can pass any number of arguments to it", and I also know it's an obsolescent feature.

But this question is quite different. The keyword is "What if". I just want to know if I pass different amount of arguments to void foo(), just like the example code above, can they be used inside foo()? If so, how is this done? If not, does the passed arguments make any difference? That's my question.

like image 475
nalzok Avatar asked Feb 16 '16 07:02

nalzok


1 Answers

As Jonathan Leffler said, C's calling convention establishes that the calling function (not the called one) is responsible for popping arguments from the stack, so the program will not crash even if the arguments do not match what the called function expects.

I'll add that C's calling convention also establishes that arguments are pushed onto the stack in reverse order (i.e. the call foo (1, 2) pushes the 2 then the 1). This allows the called function to access the first parameters even if it doesn't know the rest. For example, a function declared int foo (int a, int b, ...) will be able to access a and b even without knowing what other parameters have been passed: a and b are just at the stack top. Accessing other arguments would require stack pointer hacks, which is just what printf does. Of course, one can easily get funny results by using different arguments from what is expected (say, printf ("%d%d", 3.5);).

So, as per the question, yes, int foo () can be called safely with any number/type of arguments, and in the 1980's it would be considered "normal practice" to use pointer hacks on the stack to access unknown parameters. When portability and readability became more and more of a concern, <stdarg.h> appeared as a portable way to implement these pointer hacks (the compiler will generate the correct code for the target platform, so it is no longer a "hack"). However, as it has been said, <stdarg.h> requires at least one parameter, so it can't help with int foo ().

like image 132
Jojonete Avatar answered Nov 08 '22 17:11

Jojonete