GCC 编译过程深度解析指南
文章目录
- GCC 编译过程深度解析指南
- 1. 编译过程总览
- 2. 阶段一:预处理 (Preprocessing)
- 2.1 技术细节
- 2.2 实践验证
- 3. 阶段二:编译 (Compilation)
- 3.1 核心流程
- 3.2 实践验证
- 4. 阶段三:汇编 (Assembly)
- 4.1 目标文件结构
- 4.2 实践验证
- 5. 阶段四:链接 (Linking)
- 5.1 静态链接 vs. 动态链接
- 5.2 内存布局
- 5.3 实践验证
- 6. 总结与参考资料
- 6.1 版本信息
- 6.2 参考资料
1. 编译过程总览
GCC (GNU Compiler Collection) 将 C 源码转换为可执行文件的过程并非一蹴而就,而是分为四个独立的流水线阶段:预处理 (Preprocessing)、编译 (Compilation)、汇编 (Assembly) 和 链接 (Linking)。

| 阶段 | 输入文件 | 输出文件 | 核心工具 | 主要任务 |
|---|---|---|---|---|
| 预处理 | .c, .h | .i | cpp | 宏展开、头文件插入、删除注释 |
| 编译 | .i | .s | cc1 | 语法分析、优化、生成汇编代码 |
| 汇编 | .s | .o | as | 将汇编指令翻译为机器码 |
| 链接 | .o, .a, .so | .out | ld | 合并段、符号解析、重定位 |
2. 阶段一:预处理 (Preprocessing)
2.1 技术细节
预处理器主要处理以 # 开头的指令。
- 宏替换: 将
#define定义的宏进行文本替换。 - 文件包含: 将
#include的头文件内容插入到当前位置。 - 条件编译: 根据
#ifdef,#if等保留或剔除代码块。 - 注释清理: 删除
//和/* ... */注释。
2.2 实践验证
使用 -E 参数查看预处理结果:
gcc -E src/example.c -o example.i
查看 example.i 的尾部,可以看到宏 PI 和 SQUARE 已经被替换:
// 原始代码: int area = SQUARE(radius) * PI;
// 预处理后: int area = ((radius) * (radius)) * 3.14159;
3. 阶段二:编译 (Compilation)
这是整个流程中最复杂、最核心的阶段。GCC 前端(Frontend)将 C 代码转换为汇编代码。
3.1 核心流程
- 词法分析 (Lexical Analysis): 将字符流转换为 Token 流。
- 语法分析 (Syntax Analysis): 生成 AST (Abstract Syntax Tree) 抽象语法树。
- 语义分析 (Semantic Analysis): 类型检查、作用域检查。
- 中间代码生成 (GIMPLE/RTL): 生成与架构无关的 IR (Intermediate Representation)。
- 代码优化: 死代码消除、循环展开、常量传播等。
- 目标代码生成: 生成特定架构(如 x86_64)的汇编代码。

3.2 实践验证
使用 -S 参数生成汇编代码:
gcc -S src/example.c -o example.s
查看 example.s,可以看到 C 语言逻辑已转换为汇编指令:
.globl main
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $5, -4(%rbp) ; radius = 5
...
call printf
4. 阶段三:汇编 (Assembly)
汇编器将人类可读的汇编指令(如 mov, add)翻译为机器可执行的二进制操作码(Opcode)。
4.1 目标文件结构
生成的 .o 文件是 ELF (Executable and Linkable Format) 格式的重定位目标文件(Relocatable Object)。它包含:
- 机器码:
.text段。 - 数据:
.data,.bss段。 - 符号表: 记录了函数和变量的名称及位置,供链接器使用。
4.2 实践验证
使用 -c 参数生成目标文件:
gcc -c src/example.c -o example.o
使用 objdump 反汇编查看机器码:
objdump -d example.o
# 输出示例:
# 0: 55 push %rbp
# 1: 48 89 e5 mov %rsp,%rbp
5. 阶段四:链接 (Linking)
链接器将多个目标文件(.o)和库文件(.a, .so)合并,生成最终的可执行文件。
5.1 静态链接 vs. 动态链接
- 静态链接 (-static): 将库代码(如
printf的实现)完整拷贝到可执行文件中。- 优点:不依赖环境,移植性好。
- 缺点:文件体积大,内存浪费。
- 动态链接 (默认): 仅记录库函数的符号和路径,运行时由操作系统加载动态库。
- 优点:文件小,共享内存,易于升级库。
5.2 内存布局
链接器决定了程序在内存中的最终布局。

5.3 实践验证
生成最终可执行文件:
gcc example.o -o example
查看链接依赖:
ldd example
# 输出: libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
6. 总结与参考资料
6.1 版本信息
- 本文演示环境:GCC 11.4.0 (Ubuntu 22.04)
- 目标架构:x86_64
6.2 参考资料
- GCC Manual (GNU Project)
- Linkers and Loaders (John R. Levine)
- Computer Systems: A Programmer’s Perspective (CSAPP)
3694

被折叠的 条评论
为什么被折叠?



