You’ve been lied to.
Not by a person, but by every compiler, every textbook, every tutorial you’ve ever seen. They all told you the same thing: “Every C program must have a main() function.”
It’s not true. It never was.
I know, because I spent a weekend breaking the rules of a language that’s been around for 50 years. And what I found was more than a hack—it was a glimpse behind the curtain of how software actually works.
main() isn’t a law of C. It’s a suggestion from your linker.
Let me show you what I mean. Here’s a C program that compiles, links, and runs—without a single main() in sight:
#include <stdio.h>
void _start() {
printf("Hello from the void\n");
_exit(0);
}
Yes, that’s a real program. Compile it with gcc -nostartfiles and watch it run. No main(). No standard library initialization. Just raw execution.
If you’re feeling a mix of exhilaration and panic, good. That’s exactly the emotion I want you to feel. Because this isn’t a bug—it’s a feature. The C standard doesn’t require main() in a freestanding environment. It’s only the hosted environment (the one you use every day) that demands it. And that demand is enforced not by the language, but by the runtime—the startup code that sets up the process before your code runs.
What you call “programming” is actually just configuring a toolchain.
Here’s the twist that makes this worth your attention: most of what you think of as “C language rules” are actually just defaults of your compiler and linker. The real rules are much simpler, much more flexible, and much more terrifying.
Take the classic example: main() returns an integer, right? But what if I told you that the actual entry point for your program is defined by the operating system’s loader, and the C runtime just wraps it? _start is the real entry point. main() is just a function that gets called by _start. If you skip the runtime, you skip main().
I saw this firsthand when I wrote a C program that directly called execve without main(). It wasn’t a “valid” C program in the standard sense—it violated the spec. But it ran. It worked. It proved that the compiler is not a lawmaker, it’s a translator.
This matters because it changes how you think about debugging, optimization, and portability. When you hit a linker error, you’re not fighting the language—you’re fighting your toolchain’s opinion. And opinions can be changed.
The most interesting C programs violate the standard in some way. That’s not a bug—it’s a badge of honor. Every real-world system, from Linux kernels to embedded firmware, lives in this gray area between “valid” and “functional.”
So here’s my challenge: go write a C program without main(). Not because it’s practical—it’s not. But because it will teach you more about how your machine actually runs than any tutorial ever could.
You’ll thank me later. If your program doesn’t crash first.
FAQ
Q: Is this a valid C program according to the C standard?
A: No. The standard defines 'hosted' environments that require main(). But in 'freestanding' environments (like embedded systems), the entry point can be anything. This program uses a freestanding approach, but it's not standard-conforming for hosted environments.
Q: Will this compile on any compiler?
A: No. It requires GCC or Clang with -nostartfiles flag. MSVC or other compilers have different startup mechanisms. The technique is platform-specific, not portable.
Q: Why would anyone do this in real code?
A: You wouldn't—unless you're writing a kernel, bootloader, or embedded firmware. The value is pedagogical: it teaches you that the language's rules are not absolute, and understanding the toolchain gives you superpowers.