12

Just wondering why this

int main(void){}

compiles and links

and so does this:

int main(int argc, char **argv){}

Why isn't it required to be one or the other?

gcc will even compile and link with one argument:

int main(int argc){}

but issue this warning with -Wall:

smallest_1.5.c:3:1: warning: ‘main’ takes only zero or two arguments [-Wmain]

I am not asking this as in "how come they allow this?" but as in "how does the caller and the linker handle multiple possibilities for main?"

Scooter
  • 6,802
  • 8
  • 41
  • 64
  • 10
    The short answer is "because the standard says so". – Jeffery Thomas Sep 13 '15 at 13:31
  • @JefferyThomas My question wasn't along the lines of "why do they allow it" but "how does it work"? How is it that the caller routine and the linker can both handle either kind of definition? – Scooter Sep 13 '15 at 14:00
  • 2
    Because the calling code can, for example, pass arguments in registers or on the stack. The two argument main uses them, while the zero argument main does nothing with them. It's that simple. Linking does not even come into the picture. – Jens Sep 13 '15 at 14:07
  • 1
    Very related [question](https://stackoverflow.com/questions/10372286/how-crt-calls-main-having-different-parameter). – edmz Sep 13 '15 at 16:05

5 Answers5

9

I am taking a Linux point of view below.

The main function is very special in the standard definition (for hosted C11 implementations). It is also explicitly known by recent compilers (both GCC & Clang/LLVM....) which have specific code to handle main (and to give you this warning). BTW, GCC (with help from GNU libc headers thru function attributes) has also special code for printf. And you could add your own customization to GCC using MELT for your own function attributes.

For the linker, main is often a usual symbol, but it is called from crt0 (compile your code using gcc -v to understand what that really means). BTW, the ld(1) linker (and ELF files, e.g. executables or object files) has no notion of types or function signatures and deals only with names (This is why C++ compilers do some name mangling).

And the ABI and the calling conventions are so defined that passing unused arguments to a function (like main or even open(2)...) does not do any harm (several arguments get passed in registers). Read the x86-64 System V ABI for details.

See also the references in this answer.

At last, you really should practically define your main as int main(int argc, char**argv) and nothing else, and you hopefully should handle program arguments thru them (at least --help & --version as mandated by GNU coding standards). On Linux, I hate programs (and I curse their programmers) not doing that (so please handle --help & --version).

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
2

Because the calling code can, for example, pass arguments in registers or on the stack. The two argument main uses them, while the zero argument main does nothing with them. It's that simple. Linking does not even enter the picture.

If you are worried about stack adjustments in the called code, the main function just needs to make sure the stack pointer is the same when it returns (and often even this is of no importance, e.g. when the ABI states that the caller is responsible for stack management).

Jens
  • 69,818
  • 15
  • 125
  • 179
1

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.

Regarding the parameters:

The first counts the arguments supplied to the program and the second is an array of pointers to the strings which are those arguments. These arguments are passed to the program by the command line interpreter. So, the two possibilities are handled as:

  1. If no parameters are declared: no parameters are expected as input.

  2. If there are parameters in main() ,they should:

    • argc is greater than zero.
    • argv[argc] is a null pointer.
    • argv[0] through to argv[argc-1] are pointers to strings whose meaning will be determined by the program.
    • argv[0] will be a string containing the program's name or a null string if that is not available. Remaining elements of argv represent the arguments supplied to the program. In cases where there is only support for single-case characters, the contents of these strings will be supplied to the program in lower-case.

In memory:

they will be placed on the stack just above the return address and the saved base pointer (just as any other stack frame).

At machine level:

they will be passed in registers, depending on the implementation.

Ziezi
  • 6,375
  • 3
  • 39
  • 49
1

The short answer: if you don't use the parameters, then you can declare main without parameters, in two ways:

int main(void)

or

int main()

The first means main is a function with no parameters. The second means main is a function with any number of parameters.

Since you don't access the parameters, both will be fine. Any compiler having "special" code to check the parameters of main is wrong. (But: main must return a value.)

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • TIL. Interestingly this is one other aspect in which C differs from C++ i.e. a function taking parameters in the definition can still be declared without any in the declaration. https://softwareengineering.stackexchange.com/a/287002/331025 – Vishal Sharma Jul 02 '20 at 05:15
1

Making it work has to do with the binary format of the executable and the OS's loader. The linker doesn't care (well it cares a little: it needs to mark the entry point) and the only caller routine is the loader.

The loader for any system must know how to bring supported binary format into memory and branch into the entry point. This varies slightly by system and binary format.


If you have a question about a particular OS/binary format, you may want to clarify.

Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117