本文完整梳理 C++ 从源代码到可运行程序的四大核心阶段:预处理 → 编译 → 汇编 → 链接,覆盖原理、指令、细节与实战示例,是理解 C++ 程序运行机制的核心笔记。
一个 .cpp 源文件要变成可执行程序,必须经过4个标准步骤,缺一不可:
源代码(.cpp/.h)
↓ 【预处理】预处理器 cpp
预处理文件(.ii)
↓ 【编译】编译器 cc1plus
汇编文件(.s)
↓ 【汇编】汇编器 as
目标文件(.o/.obj)
↓ 【链接】链接器 ld
可执行文件(.out/.exe)
工具链:GCC/Clang 会自动调用上述 4 个工具,我们只需一条编译命令即可完成全流程。
预处理是编译前的文本替换工作,由预处理器完成,不检查语法,只做纯文本处理。
单行注释 //、多行注释 /* */ 全部删除。
#include <xxx> / #include "xxx" 会把头文件内容完整复制到当前文件。
#define 定义的宏,进行文本替换(不做计算)。
根据 #ifdef / #ifndef / #else / #endif 决定保留/删除代码。
方便后续报错定位。
| 指令 | 作用 |
|---|---|
#include | 插入头文件内容 |
#define | 定义宏 |
#undef | 取消宏定义 |
#ifdef / #ifndef | 判断宏是否存在 |
#if / #else / #endif | 条件编译 |
#pragma once | 防止头文件重复包含 |
#pragma message | 编译时打印信息 |
#error | 强制报编译错误 |
使用 GCC/Clang 生成预处理后的 .ii 文件:
# 只执行预处理,生成 main.ii g++ -E main.cpp -o main.ii clang++ -E main.cpp -o main.ii
打开 main.ii 可以看到:
预处理文件 → 汇编代码,是 C++ 最核心阶段。
报错:缺少分号、类型不匹配、函数未定义等。
根据 -O1/-O2/-O3 优化执行效率、体积。
输出对应平台汇编指令(人类可读的底层指令)。
# 执行到编译阶段,生成汇编文件 main.s g++ -S main.ii -o main.s # 或直接从源码一步生成 g++ -S main.cpp -o main.s
main.s 是汇编代码,包含 mov、add、call 等底层指令。
汇编代码 → 二进制目标文件,由汇编器 as 完成。
.o,Windows:.obj)# 执行到汇编阶段,生成 main.o g++ -c main.s -o main.o # 直接从源码生成 .o g++ -c main.cpp -o main.o
把多个目标文件 + 静态库/动态库 → 单个可执行文件。
给函数、变量分配最终内存地址
找到 cout、printf、std::string 等标准库实现
Linux:a.out,Windows:.exe
| 类型 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 静态链接 | 把库代码复制到可执行文件 | 不依赖外部文件,可直接运行 | 文件体积大,更新库需重新编译 |
| 动态链接 | 只记录库路径,运行时加载 | 文件小,库可共享更新 | 运行必须依赖对应库文件 |
# 链接 .o 文件,生成可执行程序 app g++ main.o -o app # 直接一键完成 4 步 g++ main.cpp -o app
运行:
./app # Linux/macOS app.exe # Windows
从源码直接执行每一步:
# 1. 预处理 → .ii g++ -E main.cpp -o main.ii # 2. 编译 → .s g++ -S main.cpp -o main.s # 3. 汇编 → .o g++ -c main.cpp -o main.o # 4. 链接 → 可执行文件 g++ main.o -o app
一键全流程:
g++ main.cpp -o app -std=c++17 -O2
#include、#define、删除注释.o).cpp| 错误 | 发生阶段 |
|---|---|
| 头文件找不到 | 预处理 |
| 宏使用错误 | 预处理 |
| 语法错误、类型错误 | 编译 |
| 函数未定义实现 | 链接 |
| 库找不到 | 链接 |