520 字
3 分钟
编译的四个步骤:预编译、编译、汇编、链接

假设我们有以下文件:

main.cpp

#include<iostream>

int main(){
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

当我们执行一条命令g++ main.cpp -o main时,实际上g++共完成了四个步骤:

1. 预处理(Preprocessing)#

g++ -E main.cpp -o main.i

或许你会注意到,C++程序中有很多以#开头的行,例如#include#define#undef#ifdef等。它们明显与我们通常写的语句不同,如它们无需以分号结尾,它们不可以直接折行。这些被称作预处理指令(Preprocessor Directive)。预处理指令就是在预处理这一步生效的。预处理具体做的工作包括引入头文件、宏的展开、注释的删除等。

2. 编译(Compiling)#

g++ -S main.i -o main.s

编译阶段将C/C++代码翻译成汇编指令,这是编译器所做的最核心、最重要的工作。编译通常包括词法分析、语法分析、语义分析几个步骤。

打开main.s,可以发现里面是汇编指令,以下是截取的一个片段。

main:
.LFB2211:
    pushq   %rbp
    .seh_pushreg  %rbp
    movq    %rsp, %rbp
    .seh_setframe %rbp, 0
    subq    $32,  %rsp
    .seh_stackalloc    32
    .seh_endprologue
    call    __main
    leaq    .LC0(%rip), %rax
    movq    %rax, %rdx
    movq    .refptr._ZSt4cout(%rip), %rax
    movq    %rax, %rcx
    call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movq    %rax, %rcx
    movq    .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rax
    movq    %rax, %rdx
    call    _ZNSolsEPFRSoS_E
    movl    $0,   %eax
    addq    $32,  %rsp
    popq    %rbp
    ret

3. 汇编(Assembling)#

g++ -c main.s -o main.o

汇编阶段将汇编指令转化成二进制文件,也就是机器码。

现在若要打开main.o文件需要使用查看二进制文件的工具,而非文本编辑器。

4. 链接(Linking)#

g++ main.o -o main

链接阶段的工作是寻找程序用到的外部文件,拼接每个模块,生成最终的可执行文件。

链接分为两种:静态链接和动态链接

静态链接就是将所需要的二进制代码全部并入最终的文件,静态链接生成的文件体积较大,但是不需要外部库的依赖。

动态链接就是在运行时再加载所需要的动态库文件,动态链接生成的文件体积小,但是需要与库文件一起发布。

编译的四个步骤:预编译、编译、汇编、链接
https://cyrus28214.github.io/posts/preprocessing-compilation-assembling-and-linking/
作者
Cyrus
发布于
2023-07-14
许可协议
CC BY-NC-SA 4.0