
写完 C++ 代码,不能直接运行,必须经过编译才能变成可执行程序。这个过程不是一步到位的,而是分阶段完成的:预处理 → 编译 → 汇编 → 链接。每一步都做特定的事,出错时也能帮你快速定位问题在哪一环。
预处理(Preprocessing):处理 # 开头的指令
这一步不涉及语法检查,只做文本替换和文件拼接。编译器会:
- 把 #include iostream> 展开成实际的头文件内容(比如把整个 iostream 的声明塞进来)
- 把 #define MAX 100 替换成数字 100,所有出现 MAX 的地方都被替换了
- 跳过被 #ifdef DEBUG 等条件屏蔽掉的代码块
你可以用 g++ -E main.cpp -o main.i 单独看预处理后的结果,生成的是一个纯文本 .i 文件,能直观看到宏展开和头文件合并后的样子。
编译(Compilation):把 C++ 翻译成汇编指令
这是真正开始“理解”你代码的阶段。编译器检查语法、类型、作用域、模板实例化等,一旦发现 int x = "hello"; 或调用未定义函数,就在这里报错。
立即学习“C++免费学习笔记(深入)”;
它输出的是人类可读的汇编代码(如 x86 或 ARM 指令),不是机器码。用 g++ -S main.cpp 可生成 .s 文件。注意:此时每个源文件是独立编译的,不关心其他文件里有没有定义某个函数。
汇编(Assembly):把汇编代码转成机器指令
汇编器(as)把上一步生成的 .s 文件逐行翻译成二进制目标文件(.o 或 .obj)。这个文件包含机器码、符号表(函数名、全局变量名)、重定位信息,但还不能运行——因为函数调用地址还没确定,外部库也没连上。
用 g++ -c main.cpp 就能得到 main.o。多个 .cpp 文件会各自生成对应的 .o 文件,互不影响。
链接(Linking):把所有零件组装成可执行文件
链接器(ld)负责三件事:
- 符号解析:找到 main.o 里调用的 std::cout::operator 在哪个库(比如 libstdc++.a)里
- 重定位:把函数调用的临时地址(如 “跳转到第 120 字节”)换成真实内存地址
- 合并段:把所有 .o 和库里的 .text(代码)、.data(已初始化全局变量)等按规则拼成最终的可执行文件
如果漏了实现(比如声明了 void foo(); 但没写定义),或者重复定义了同一个全局变量,链接阶段才会报错(undefined reference / multiple definition)。
基本上就这些。理解这四步,遇到编译错误时就能判断是写错了(编译阶段)、少包含了头文件(预处理)、忘了写函数体(链接),还是路径/库名配错了(链接)。不复杂但容易忽略。










