Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When main is defined without parameters, will argc and argv still be present on the stack?

Tags:

Consider the very simple:

int main(void) {     return 0; } 

I compiled it (with mingw32-gcc) and executed it as main.exe foo bar.

Now, I had expected some sort of crash or error caused by a main function explicitly declared as being bereft of life parameters. The lack of errors led to this question, which is really four questions.

  • Why does this work? Answer: Because the standard says so!

  • Are the input parameters just ignored or is the stack prepared with argc & argv silently? Answer: In this particular case, the stack is prepared.

  • How do I verify the above? Answer: See rascher's answer.

  • Is this platform dependant? Answer: Yes, and no.

like image 664
manneorama Avatar asked Jun 29 '10 20:06

manneorama


People also ask

Can we define a function without the parameter?

Per the letter of the standard, yes Even though both T f() { ... } and T f(void) { ... } define a function with no parameters, these definitions are not 100% equivalent: the first form doesn't provide function prototype.

Why do you need to use argc and argv [] in your main function and what does it do?

Here, argc (argument count) stores the number of the arguments passed to the main function and argv (argument vector) stores the array of the one-dimensional array of strings. So, the passed arguments will get stored in the array argv and the number of arguments will get stored in the argc .

When the main function is called it is called with the arguments argc and argv?

The first parameter, argc (argument count) is an integer that indicates how many arguments were entered on the command line when the program was started. The second parameter, argv (argument vector), is an array of pointers to arrays of character objects.

Can main () have parameters?

Yes, we can give arguments in the main() function.


2 Answers

I don't know the cross-platform answer to your question. But this made me curious. So what do we do? Look at the stack!

For the first iteration:

test.c

int main(void) {    return 0; } 

test2.c

int main(int argc, char *argv[]) {    return 0; } 

And now look at the assembly output:

$ gcc -S -o test.s test.c  $ cat test.s          .file   "test.c"         .text .globl main         .type   main, @function main:         pushl   %ebp         movl    %esp, %ebp         movl    $0, %eax         popl    %ebp         ret         .size   main, .-main         .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"         .section        .note.GNU-stack,"",@progbits 

Nothing exciting here. Except for one thing: both C programs have the same assembly output!

This basically makes sense; we never really have to push/pop anything off of the stack for main(), since it's the first thing on the call stack.

So then I wrote this program:

int main(int argc, char *argv[]) {    return argc; } 

And its asm:

main:         pushl   %ebp         movl    %esp, %ebp         movl    8(%ebp), %eax         popl    %ebp         ret 

This tells us that "argc" is located at 8(%ebp)

So now for two more C programs:

int main(int argc, char *argv[]) { __asm__("movl    8(%ebp), %eax\n\t"         "popl    %ebp\n\t"         "ret");         /*return argc;*/ }   int main(void) { __asm__("movl    8(%ebp), %eax\n\t"         "popl    %ebp\n\t"         "ret");         /*return argc;*/ } 

We've stolen the "return argc" code from above and pasted it into the asm of these two programs. When we compile and run these, and then invoke echo $? (which echos the return value of the previous process) we get the "right" answer. So when I run "./test a b c d" then $? gives me "5" for both programs - even though only one has argc/argv defined. This tells me that, on my platform, argc is for sure placed on the stack. I'd bet that a similar test would confirm this for argv.

Try this on Windows!

like image 163
poundifdef Avatar answered Nov 08 '22 07:11

poundifdef


From the C99 standard:

5.1.2.2.1 Program startup

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

int main(void) { /* ... */ } 

or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

int main(int argc, char *argv[]) { /* ... */ }

or equivalent; or in some other implementation-defined manner.

like image 45
Tim Schaeffer Avatar answered Nov 08 '22 08:11

Tim Schaeffer