Thibaud explains… code compilation— What happens when you type “gcc main.c”?

Thibaud Poncin
4 min readSep 16, 2020
You CAN teach an old computer new tricks, but that won’t happen if you ask in English.

Say you want your computer to do something new. Can you ask it, in natural language, what you want? “Duh, of course not!” I hear you say, “my computer speaks ‘code’, not English!”

And you’d be kinda-sorta-half-right. On a hardware level, all your computer knows is machine code, aka binary, aka 1s and 0s. At any given time, in any given part of your computer, either current is flowing or it’s not, and that’s all it needs to its computy thingy magic. Add a few extra steps to this, and you get your Aunties’ Flat Earther Facebook page. But I digress.

So do all programmers have to write 0s and 1s to make computers compute, then? Buddy, it’s your lucky day: your billion dollar app idea won’t have to be written in machine code. Not by you, at least. (I mean you can, but trust me, you don’t want to).

Let’s assume we’re writing your app in C. The path from your source code to the executable file your millions of customers will be launching on their machine is long, but pretty straightforward, and that’s what we’re gonna cover in this article.

So, what happens when you type gcc main.c?

Compiling C: a 4 step process

C compilation is a 4-step program. That’s 8 less than AA and you’re allowed to drink when they fail!

A lot is happening when you hit that command. In this article, we’ll first talk in depth about what actually happens behind the scene, then we’ll run a quick example and see what you’ll see in your shell as a user typing this command.

Let’s quickly breakdown what the actual gcc main.c command means, and then we’ll explain what happens when you use it.

gcc calls GCC, or the GNU C Compiler, with the input file main.c . This input file contains our C source code (as is the norm for all .c files).

In short, we’re asking GCC, our C compiler, to fetch the code we wrote in main.c, compile it, and output an executable program based on our code.

TL;DR: the compiler

  • reads the source file,
  • processes it in a way that your computer can understand more easily,
  • links it with whatever runtime libraries are required,
  • creates an executable binary file.

Since that’s a lot to “process” (pun absolutely intended), let’s unpack those steps in more detail.

Step 1: Preprocessing

The first module of the compiler is the Preprocessor. The preprocessor takes your code, usually stored in a .c file, and does a bit of formatting and housekeeping in it: it strips all the comments from your code, includes the header file(s) you asked for with the #include <whatever.h> , etc.

The output of this step is still readable C source code, which is usally fed directly into the compiler. You can use the command gcc -E main.cto run your code through the preprocessor only.

Step 2: Compilation

The second module is the Compiler. At this stage, the Compiler translates the source code into assembly language, which is an intermediate language that’s very close to machine code, but still readable by (albeit crazy) humans. When launched on its own (with gcc -S main.c), GCC will run the preprocessor and the compiler only, and output the result to the main.s file.

Step 3: Assembling

In this third step, our compiled code goes through the Assembling phase. This step will take the assembly code we got in the previous step as input, and turn it into an object file in machine code (or binary). As previous steps, GCC can be asked to stop after this process, using the gcc -c main.c command, which will output the unlinked binary to the main.o file.

Step 4: Linking

For our final step, the Linker will take our object file(s) (our code + the required libraries) and link them together (either statically or dynamically) to create an executable file, usually called a.out . Tada! We have our executable file! :)

Example

We’ll run gcc main.c on our Linux machine. Here’s the contents of main.c :

#include <stdio.h>/**
* main - Prints "Hello, Medium!"
*
*
* Return: Always 0
*/
int main(void)
{
printf("Hello, Medium!\n");
return (0);
}

Let’s run gcc main.c !

No turning back now!

…Wow, that was anticlimactic. We’re just sent back to our prompt. But wait! Something appeared in our folder!

Is it a bird? Is it a plane ? It’s not a birdplane, but it’s a wild a.out!

Our standard executable output is there (and it’s executable, as the chmod 775 can attest). Let’s run it with ./a.out !

Great Success!

Our file works! Compilation works! gcc main.c works! EVERYTHING WORKS!

--

--