Thibaud explains… code compilation— What happens when you type “gcc main.c”?
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
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.c
to 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
!
…Wow, that was anticlimactic. We’re just sent back to our prompt. But wait! Something appeared in our folder!
Our standard executable output is there (and it’s executable, as the chmod 775 can attest). Let’s run it with ./a.out
!
Our file works! Compilation works! gcc main.c
works! EVERYTHING WORKS!