# 的操作(include/define/ifdef)→ 预处理;语法/类型检查、C 转汇编 → 编译;生成 .o 机器码 → 汇编;跨文件符号合并、地址重定位、undefined reference → 链接一句话:"# 开头的预处理,语法类型的编译,.o 的汇编,符号的链接。"
.c → 可执行程序四步:预处理(文本处理)→ 编译(C 转汇编)→ 汇编(汇编转机器码 .o)→ 链接(合并 .o + 库 → 可执行)。每步只干自己那摊事,报错也按阶段定位。
hello.c
→ 预处理(cpp):展开 #include/#define/条件编译/删注释/加行号 → hello.i
→ 编译(cc):C 语言 → 汇编指令(语法/类型检查在这) → hello.s
→ 汇编(as):汇编 → 机器码 → hello.o(目标文件,还不能跑)
→ 链接(ld):合并 hello.o + 库/启动代码 → hello(可执行)
| 阶段 | 工具 | 输入→输出 | 干什么 |
|---|---|---|---|
| 预处理 | cpp | .c → .i | 展开宏、包含头文件、条件编译、删注释、加行号 |
| 编译 | cc | .i → .s | C 转汇编(语法/类型检查) |
| 汇编 | as | .s → .o | 汇编转机器码 |
| 链接 | ld | .o → 可执行 | 合并符号、地址重定位 |
| 操作/报错 | 属于哪步 |
|---|---|
#include / #define / #ifdef 展开 |
预处理 |
| 头文件找不到(include 报错) | 预处理 |
| 语法检查、类型检查 | 编译 |
#define MAX 1O0(字母 O)内容错误 |
编译(预处理只替换不查内容,编译解析时才报) |
| C 语言转汇编指令 | 编译 |
| 汇编转机器码(生成 .o) | 汇编 |
| 符号合并、地址重定位 | 链接 |
undefined reference 报错 |
链接 |
| 内联函数展开 | 编译(优化阶段) |
静态库 .a/.lib |
动态库 .so/.dll |
|
|---|---|---|
| 链接时 | 用到的目标代码合并进可执行文件 | 只记录引用,不拷代码 |
| 可执行文件 | 变大 | 小 |
| 运行时 | 独立,不依赖库文件 | 依赖 .so 存在,由加载器运行时加载 |
| 更新库 | 要重新链接 | 换 .so 即可,不用重编译 |
.o 能直接运行吗 → 不能,缺启动文件、库、入口函数,要链接#define MAX 1O0(字母 O)→ 编译阶段发现问题(预处理只做文本替换不查内容)| 相邻概念 | 和本主题的关系 | 孤立理解会犯的错 |
|---|---|---|
| extern | undefined reference 就发生在链接阶段 | 以为 undefined reference 是编译错 |
| 程序分段 | 链接产物 = 分段图的基础(text/data/bss) | 以为分段是编译期定的 |
| 内联函数 | 内联展开在编译优化阶段 | 以为内联是预处理/链接干的事 |
| 静态/动态库 | 链接阶段:.a 合并进程序,.so 运行时加载 | 把静态库/动态库记反 |
2026-08-03
每主题一页,复习时只翻本目录。