main
is called by some startup function in the C runtime library. The C language standard says that returning from main
is equivalent to calling the exit
function, so most C runtimes look something like this:
void _start(void) /* Exact function signature may vary */
{
/* Platform-specifi startup (e.g. fetch argc and argv from the OS) */
...
int status = main(argc, argv);
exit(status);
/* Never reached */
}
The exit status gets passed back to the operating system, and then what happens from there is OS-dependent.
When you compile and link your program, the executable file format (e.g. PE or ELF) contains a start address, which is the virtual address at which execution begins. That function is typically part of the C runtime library (like the example _start
above). That function has to end by calling a system call such as exit
, since if it just returned, it would have nowhere to go to: it would just pop an address off the stack and jump to that location, which would be garbage.
Depending on how the OS loader initializes processes, the program arguments argc
, argv
, and other data (such as the environment) might either come in as function parameters (either through registers or the stack), or they might require system calls (e.g. GetCommandLine
on Windows) to retrieve them. But dealing with all of that is the job of the C runtime, and unless you're explicitly going out of your way to avoid using the C runtime, you needn't worry about those details.