Take the 2-minute tour ×

We can write main function in several ways,

  1. int main()
  3. int main(int argc,char *argv[],char * environment)

How run-time CRT function knows which main should be called. Please notice here, I am not asking about Unicode supported or not.

share|improve this question
add comment

5 Answers

up vote 4 down vote accepted

The accepted answer is incorrect, there's no special code in the CRT to recognize the kind of main() declaration.

It works because of the cdecl calling convention. Which specifies that arguments are pushed on the stack from right to left and that the caller cleans up the stack after the call. So the CRT simply passes all arguments to main() and pops them again when main() returns. The only thing you need to do is specify the arguments in the right order in your main() function declaration. The argc parameter has to be first, it is the one on the top of the stack. argv has to be second, etcetera. Omitting an argument makes no difference, as long as you omit all the ones that follow as well.

This is also why the printf() function can work, it has a variable number of arguments. With one argument in a known position, the first one.

share|improve this answer
This is probably accurate 32 bit Windows C runtimes. But what about on other platforms where the ABI differs? For example x64 where parameters are passed in registers even for cdecl. And what about a conforming main function declared like this int main(double d)? And where in the standard does it state that all main functions must use __cdecl which is not even part of the standard? And what about WinMain? –  David Heffernan Apr 29 '12 at 13:35
Well, at least the first sentence of your answer is now accurate. ;-) –  David Heffernan Apr 29 '12 at 13:47
Sour grapes David? Your answer might be correct, I just don't know any compiler that supports the kind of reflection that would be required to make it work. I can only offer what I've seen used in 8 of them that I studied closely enough, they all used a cdecl calling convention. Probably because it is very easy to implement, albeit not so easy to generate the code. Reflection is a very uncommon feature in C. You can perhaps make it more convincing by giving an example of such a compiler? –  Hans Passant Apr 29 '12 at 14:04
Sour grapes? Maybe a little. But I mostly think your answer is wrong in general. On x64, parameters are not pushed onto the stack. And there are other platforms that use registers for parameter passing. There's no need for reflection. The fact that I can't give an example of an extant compiler that has int main(double d) as an accepted main doesn't really matter. The standard allows it. –  David Heffernan Apr 29 '12 at 14:09
The gist of the answer remains right, even though it's technically incorrect. The exact signature doesn't matter if and only if each parameter is uniquely mapped to one storage location (register or memory) regardless of other parameters. –  MSalters Apr 29 '12 at 21:55
show 1 more comment

Hmmm. It seems that perhaps the currently accepted answer, which indicates that the previously accepted answer is incorrect, is itself incorrect. The tags on this question indicate it applies to C++ as well as C, so I’ll stick to the C++ spec, not C99. Regardless of all other explanations or arguments, the primary answer to this question is that “main() is treated special in an implementation-defined way.” I believe that David's answer is technically more correct than Hans', but I'll explain it in more detail....

The main() function is a funny one, treated by the compiler & linker with behavior that matches no other function. Hans is correct that there is no special code in the CRT to recognize different signatures of main(), but his assertion that it “works because of the cdecl calling convention” applies only to specific platform(s), notably Visual Studio. The real reason that there’s no special code in the CRT to recognize different signatures of main() is that there’s no need to. And though it’s sort of splitting hairs, it’s the linker whose job it is to tie the startup code into main() at link time, it’s not the CRT’s job at startup time.

Much of how the main() function is treated is implementation-defined, as per the C++ spec (see Section 3.6, “Start and termination”). It’s likely that most implementations’ compilers treat main() implicitly with something akin to extern “C” linkage, leaving main() in a non-decorated state so that regardless of its function prototype, its linker symbol is the same. Alternatively, the linker for an implementation could be smart enough to scan through the symbol table looking for any whose decorated name resolves to some form of “[int|void] main(...)” (note that void as a return type is itself an implementation-specific thing, as the spec itself says that the return type of main() must be ‘int’). Once such a function is found in the available symbols, the linker could simply use that where the startup code refers to “main()”, so the exact symbol name doesn’t necessarily have to match anything in particular; it could even be wmain() or other, as long as either the linker knows what variations to look for, or the compiler endows all of the variations with the same symbol name.

Also key to note is that the spec says that main() may not be overloaded, so the linker shouldn’t have to “pick” between multiple user implementations of various forms of main(). If it finds more than one, that’s a duplicate symbol error (or other similar error) even if the argument lists don’t match. And though all implementations “shall” allow both




they are also permitted to allow other argument lists, including the version you show that includes an environment string array pointer, and any other variation that makes sense in any given implementation.

As Hans indicates, the Visual Studio compiler’s cdecl calling convention (and calling conventions of many other compilers) provide a framework wherein a caller can set up the calling environment (i.e. the stack, or ABI-defined registers, or some combination of the two) in such a way that a variable number of arguments can be passed, and when the callee returns, the caller is responsible for cleanup (popping the used argument space off the stack, or in the case of registers, nothing needs done for cleanup). This setup lends itself neatly to the startup code passing more parameters than might be needed, and the user’s main() implementation is free to use or not use any of these arguments, as is the case with many platforms’ treatment of the various forms of main() you list in your question. However, this is not the only way a compiler+linker could accomplish this goal: Instead, the linker could choose between various versions of the startup code based on the definition of your main(). Doing so would allow a wide variety of main() argument lists that would otherwise be impossible with the cdecl caller-cleanup model. And since all of that is implementation-defined, it’s legal per the C++ spec, as long as the compiler+linker supports at least the two combinations shown above (int main() and int main(int, char**)).

share|improve this answer
add comment

The C 99 Standard ( Program startup) says that an implementation enforces no prototype for the main() function, and that a program can define it as either of:

1) int main(void);

2) int main(int argc, char *argv[]);

or in a manner semantically equivalent to 2), e.g.

2') int main(int argc, char **argv);

or in other implementation defined ways. It does not mandate that the prototype:

3) int main(int argc, char *argv[],char * envp[]);

will have the intended behaviour - although that prototype must compile, because any prototype must compile. 3) is supported by GCC and Microsoft C among other compilers. (N.B. The questioner's 3rd prototype has char *envp rather than char *envp[], whether by accident or because he/she has some other compiler).

Both GCC and Microsoft C will compile main() with any prototype whatsoever, as they ought to. They parse the prototype that you actually specify and generate assembly language to consume the arguments, if any, in the correct manner. Thus for example they will each generate the expected behaviour for the program:

#include <stdio.h>

void main(double d, char c)

if you could find a way of passing a double and a char directly to the program, not via an array of strings.

These observations can be verified by enabling the assembly language listings for experimental programs.

The question of how the compiler's standard CRT permits us to invoke the generated implementation of main() is distinct from the question of how main() may be defined to the compiler.

For both GCC and MS C, main() may defined any way we like. In each case however the implemention's standard CRT, AFIK, supports passing arguments to main() only than as per 3). So 1) - 2') will also have the expected behavior by ignoring excess arguments, and we have no other options short of providing a non-standard runtime of our own.

Hans Passant's answer seems incidentally misleading in suggesting that argc tells the function how many subsequent arguments to consume in the same manner as the first argument to printf(). If argc is present at all, it only denotes the number of elements in the the array passed as the second argument argv. It does not indicate how many arguments are passed to main(). Both GCC and MS C figure out how what arguments are expected by parsing the prototype that you write - essentially what a compiler does with any function except those, like printf(), that are defined to take a variable number of arguments.

main() does not take a variable number of arguments. It takes the arguments you specify in your definition, and the standard CRTs of the usual compilers assume them to be (int, char *[], char *[]).

share|improve this answer
add comment

In general, the compiler/linker would need to recognise the particular form of main that you are using and then include code to adapt that from the system startup function to your C or C++ main function.

It is true that specific compilers on specific platforms could get away without doing this, using the methods that Hans describes in his answer. However, not all platforms use the stack to pass parameters, and it is possible to write conforming C and C++ implementations which have incompatible parameter lists. For such cases, then the compiler/linker would need to determine which form of main to call.

share|improve this answer
I agree, but how it recognize and generate the code, even if we take it as overloading, C doesn't supports it. –  pranitkothari Apr 29 '12 at 13:05
Thanks. Answer accepted. –  pranitkothari Apr 29 '12 at 13:13
You got this one wrong, check my answer. –  Hans Passant Apr 29 '12 at 13:28
@Hans It depends on the implementation. You are talking about the C runtimes that you know. But the C standard doesn't back you up. And what you say about calling conventions is also off the mark. Not all platforms use the same binary ABI. –  David Heffernan Apr 29 '12 at 13:30
add comment

First, the main function is treated specifically in GCC (e.g. the main_identifier_node in file gcc/c-family/c-common.c of the source tree of GCC 4.7)

And the C11 and C++11 standards have specific wording and specification about it.

Then, the C calling ABI conventions are usually so that extra arguments don't harm much.

So you can think of it as if both the language specification and the compiler have specific things regarding "overloading" of main.

I even think that main might not be an ordinary function. I believe that some words in the standard -which I don't have right now- might be e.g. understood as forbidding taking its address or recursing on main.

In practice, main is called by some assembly code compiled into crt*.o files linked by gcc. Use gcc -v to understand more what is happenning.

share|improve this answer
add comment

Your Answer


