前言

由于经常接触编译期,运行期,链接期,在此简单记录C++从源代码到可执行文件的过程


🧩 1. 预处理阶段(Preprocessing)

时机:编译前,由预处理器执行
任务

  • 处理 #include, #define, #ifdef 等指令
  • 宏展开、条件编译、文件合并

结果:生成一个单一的、纯 C++ 源码文件(无宏、无包含指令)。

✍️ 我的思考:
我们熟悉的头文件插入就是在这里完成的。

🛠️ 2. 编译阶段(Compilation)

时机:预处理后,由编译器执行
任务

  • 语法分析、语义检查(如类型检查)
  • 每个 .cpp 文件编译为目标文件(.o.obj
  • 生成机器指令,但尚未解析符号

结果:每个源文件变成一个目标文件

关键词编译期错误(如语法错误、类型不匹配)

✍️ 我的思考:
类似于对代码做一遍检查,在把代码整理成指令串的同时看看有没有错误。
例如,对没有虚函数的类进行`dynamic_cast`就会在这里报错

🔗 3. 链接阶段(Linking)

时机:编译完成后,由链接器执行
任务

  • 合并多个目标文件为一个可执行程序
  • 解析函数/变量符号地址
  • 链接标准库、用户库

结果:生成可执行文件(如 .exe 或无扩展名的程序)

关键词链接错误(如 undefined reference)

✍️ 我的思考:
出现同名类/错误定义导致找不到类的时候就会报错
相当于顺着编译后给出的“指令”执行,却无法找到正确的路
如果声明函数,但不定义,就会产生这种结果

🚀 4. 运行时阶段(Runtime / Execution)

时机:程序启动后,由操作系统加载器和 CPU 执行
任务

  • 加载程序到内存
  • 初始化全局/静态对象
  • 执行 main() 函数
  • 处理内存、控制流、异常等
  • 处理 typeid, dynamic_cast 等操作,借助编译器生成的类型信息表工作
    关键词运行时错误(如除以零、空指针访问)
    ✍️ 我的思考:
    经典错误:数组越界/指针越界以及错误的折构等等

🧹 5. 程序终止阶段(Termination / Destruction)

时机main() 退出或程序结束时
任务

  • 调用 atexit() 注册的函数
  • 析构全局和静态对象
  • 回收资源(内存、文件等)
✍️ 我的思考:
`atexit()`主要在IO操作里面使用
非new的内存存在栈中,在这个时候被按顺序回收

✅ 整体流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
源码.cpp


[预处理] ──> .i 文件(纯C++展开文件)


[编译] ──> .o 或 .obj 文件(机器码片段)


[链接] ──> 可执行文件(如 .exe)


[运行时] ──> 加载 → 初始化 → 执行 main() → 程序逻辑


[终止] → 析构清理

❗ 各阶段常见错误类型对照表

阶段 错误类型 示例
预处理 宏未定义、文件缺失 #include "xxx.h" 找不到文件
编译 语法/类型错误 int x = "abc";
链接 符号未定义等 undefined reference to 'foo'
运行时 空指针、除零等 segmentation fault, abort()
RTTI 类型转换失败 dynamic_cast 抛出 std::bad_cast