Every beginner C tutorial uses void main() at some point. Some compilers accept it. But it is wrong — not just stylistically, but technically. Understanding why explains how C programs interact with the operating system and why the return value of main is not optional.
The Standard Says int main()
The C standard defines exactly two valid signatures for main:
int main(void) /* no arguments */
int main(int argc, char *argv[]) /* with command-line arguments */Everything else — void main(), int main(int argc, char **argv, char **env), int main(void, ...) — is implementation-defined at best and undefined behavior at worst. The C11 standard (section 5.1.2.2.1) is explicit: “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.”
The Return Value Is Exit Status
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Hellon");
return 0; /* success — equivalent to EXIT_SUCCESS */
}
$ ./hello
Hello
$ echo $?
0int main() {
/* something failed */
return 1; /* or EXIT_FAILURE for portable code */
}$ ./fail
$ echo $?
1The return value of main becomes the process exit status, visible to the shell via $? on Unix or %ERRORLEVEL% on Windows. Shell scripts, CI systems, and tools that call your program all depend on this to know whether the program succeeded. A void main() cannot communicate success or failure.
Why Compilers Accept void main()
/* GCC accepts this but emits a warning */
void main() {
printf("works, sort ofn");
}gcc -Wall program.c
/* warning: return type of 'main' is not 'int' [-Wmain] */GCC and MSVC accept void main() as an extension for backwards compatibility with old code. Some embedded systems compilers mandate it because the startup code doesn’t use a return value. On hosted implementations (any normal PC or server), it is wrong.
Omitting return — The C99 Special Case
#include <stdio.h>
int main() {
printf("no return statementn");
/* reaching the end of main() implicitly returns 0 in C99 and later */
}Since C99, falling off the end of main without a return is defined to return 0 (success). This only applies to main — not to any other function. In any other int-returning function, missing a return statement is undefined behavior.
EXIT_SUCCESS and EXIT_FAILURE
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <filename>n", argv[0]);
return EXIT_FAILURE; /* 1 on most systems */
}
/* ... */
return EXIT_SUCCESS; /* 0 on all systems */
}EXIT_SUCCESS is always 0. EXIT_FAILURE is implementation-defined (usually 1). Use these macros for portable code — they document intent clearly and work across all platforms.
The env Parameter
#include <stdio.h>
/* Non-standard but widely supported POSIX extension */
int main(int argc, char *argv[], char *envp[]) {
for (int i = 0; envp[i] != NULL; i++) {
printf("%sn", envp[i]); /* prints all environment variables */
}
return 0;
}Many platforms support a third parameter char *envp[] containing environment variables. This is not in the C standard — it’s a POSIX extension. For portable access to environment variables, use getenv("PATH") from <stdlib.h> instead.
exit() vs return from main
#include <stdlib.h>
#include <stdio.h>
void cleanup(void) {
printf("atexit cleanupn");
}
int main() {
atexit(cleanup);
/* Both call atexit handlers and flush stdio buffers */
return 0;
/* OR: exit(0); -- equivalent to return 0 from main */
/* _exit(0) skips atexit and stdio flush -- for signal handlers or after fork() */
}return 0 from main and exit(0) are equivalent — both call atexit handlers, flush stdio buffers, and return the exit status to the OS. Use return for normal paths; use exit() when you need to terminate from a function deep in the call stack.
To confirm the difference between int main() and void main() firsthand, compile both in our c online compiler with -Wall and compare the output. See also our guide on GCC flags for the warning flags that catch incorrect main signatures.
TL;DR
- The C standard defines two valid main signatures:
int main(void)andint main(int argc, char *argv[]) void main()is wrong on hosted platforms — it’s an extension accepted by some compilers for legacy compatibility- The return value of
mainbecomes the process exit status — visible to the shell and CI systems - Use
EXIT_SUCCESSandEXIT_FAILUREfrom<stdlib.h>for portable exit codes - In C99+, falling off the end of
mainreturns 0 implicitly — but not in any other function return 0from main andexit(0)are equivalent;_exit(0)skips cleanup- Compile with
-Wall— GCC emits-Wmainfor non-standard main signatures