The Mystery of C++ Program Compilation (Part 3)

What really happens internally when we write a program in an IDE and click the compile button? Why does it generate an executable file? What steps are involved in this process? Is it simple or complex? In this article, we will clarify these matters.

The Mystery of C++ Program Compilation (Part 3)

First, it is important to clarify that compilation is just a general term. The entire compilation process includes preprocessing, compilation, assembly, and linking.

We provide a very simple program.

//test.c#include <stdio.h>#define max 5int main(){    printf("max = %d\n", max);    return 0;}

1. Preprocessing

The directives in the preprocessing stage generally start with #, replacing the included header files with #include, replacing macros defined with #define, deleting comments, and removing parts of the code that do not meet conditions indicated by #ifdef. All code starting with # is processed during the preprocessing stage.

Preprocessing command: gcc -E test.c -o test.i

The -E option here makes the program stop after preprocessing is complete, for the sake of our observation later. When we list the current directory with ls, we can see an additional file named test.i. Opening it reveals many declarations of variables, functions, etc., which are the results of expanding the stdio.h header file. At the end, we can see that our defined macro max has been replaced with 5.

The Mystery of C++ Program Compilation (Part 3)

2. Compilation

Students majoring in computer science in college will definitely study a course called “Compiler Principles”. This course often frustrates many students. The compilation process is precisely what is covered in Compiler Principles, including lexical analysis, syntax analysis, semantic analysis, program optimization, and a series of processes. These are the core contents of the compiler. If you want to develop a compiler, you must be very proficient in this process! This process compiles the program into assembly language, which is closer to machine language. Errors and warnings we often see when compiling in an IDE usually occur during this process.

Compilation command: gcc -S test.i -o test.s

The -S option here makes the program stop after compilation is complete, for the sake of our observation later. When we list the current directory with ls, we can see an additional file named test.s. Opening it reveals a bunch of assembly instructions. I can’t understand these instructions at all. To be honest, without prior exposure to assembly language, most people will find it incomprehensible. However, if you want to excel in the low-level field of compilers, you must understand assembly language.

The Mystery of C++ Program Compilation (Part 3)

3. Assembly

Assembly language can be understood by some professionals, but computers cannot understand it at all. Computers can only understand machine language like 010101. Therefore, we also need to convert assembly language into machine language. As for how this process works, it is beyond the scope of this article and I cannot discuss it because I do not know. These are the areas of research for those highly skilled experts. I am not exaggerating the difficulty here; those who can develop commercial compilers are definitely the chosen ones in the field of computer science.

Assembly command: gcc -C test.i -o test.o

When we list the current directory with ls, we can see an additional file named test.o. Opening it reveals a bunch of garbled characters. In fact, these are binary commands, which are the commands that computers can understand.

4. Linking

Even though binary files can be understood by computers, if your source file uses functions from other header files you have written yourself, or from third-party static or dynamic libraries, you still need to link them together to generate an executable file so that it can be executed correctly.

Linking command: gcc test.o -o test

However, if the included header files are those that come with C/C++, in other words, if there is only one source file, it seems unnecessary to perform this linking step; you can directly run the .o file generated from the above compilation. In fact, performing the linking operation will result in this error, and I have not yet found the reason. If anyone knows, feel free to leave a comment.

/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: error in test.o(.eh_frame); no .eh_frame_hdr table will be created

The above are the several steps of compilation. Only by clearly mastering each step can one truly understand the entire compilation process. Of course, you can also compile in one go:

gcc test.c -o test

This command can directly generate an executable file.

For more exciting content, please follow the public account: A Little Moonlight

The Mystery of C++ Program Compilation (Part 3)

For related articles on compilation, please read:

The Mystery of C++ Program Compilation (Part 1) – The Strange Phenomenon of Multi-file Compilation

The Mystery of C++ Program Compilation (Part 2) – The Secrets of Hidden Source Code, Dynamic and Static Linking Libraries

Leave a Comment