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 $?
0
int main() {
    /* something failed */
    return 1;           /* or EXIT_FAILURE for portable code */
}
$ ./fail
$ echo $?
1

The 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) and int 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 main becomes the process exit status — visible to the shell and CI systems
  • Use EXIT_SUCCESS and EXIT_FAILURE from <stdlib.h> for portable exit codes
  • In C99+, falling off the end of main returns 0 implicitly — but not in any other function
  • return 0 from main and exit(0) are equivalent; _exit(0) skips cleanup
  • Compile with -Wall — GCC emits -Wmain for non-standard main signatures