C++源码运行
前言
由于经常接触编译期,运行期,链接期,在此简单记录C++从源代码到可执行文件的过程
🧩 1. 预处理阶段(Preprocessing)
时机:编译前,由预处理器执行
任务:
- 处理
#include
,#define
,#ifdef
等指令 - 宏展开、条件编译、文件合并
结果:生成一个单一的、纯 C++ 源码文件(无宏、无包含指令)。
✍️ 我的思考:
我们熟悉的头文件插入就是在这里完成的。
我们熟悉的头文件插入就是在这里完成的。
🛠️ 2. 编译阶段(Compilation)
时机:预处理后,由编译器执行
任务:
- 语法分析、语义检查(如类型检查)
- 每个
.cpp
文件编译为目标文件(.o
或.obj
) - 生成机器指令,但尚未解析符号
结果:每个源文件变成一个目标文件
关键词:编译期错误(如语法错误、类型不匹配)
✍️ 我的思考:
类似于对代码做一遍检查,在把代码整理成指令串的同时看看有没有错误。
例如,对没有虚函数的类进行`dynamic_cast`就会在这里报错
类似于对代码做一遍检查,在把代码整理成指令串的同时看看有没有错误。
例如,对没有虚函数的类进行`dynamic_cast
🔗 3. 链接阶段(Linking)
时机:编译完成后,由链接器执行
任务:
- 合并多个目标文件为一个可执行程序
- 解析函数/变量符号地址
- 链接标准库、用户库
结果:生成可执行文件(如 .exe
或无扩展名的程序)
关键词:链接错误(如 undefined reference)
✍️ 我的思考:
出现同名类/错误定义导致找不到类的时候就会报错
相当于顺着编译后给出的“指令”执行,却无法找到正确的路
如果声明函数,但不定义,就会产生这种结果
出现同名类/错误定义导致找不到类的时候就会报错
相当于顺着编译后给出的“指令”执行,却无法找到正确的路
如果声明函数,但不定义,就会产生这种结果
🚀 4. 运行时阶段(Runtime / Execution)
时机:程序启动后,由操作系统加载器和 CPU 执行
任务:
- 加载程序到内存
- 初始化全局/静态对象
- 执行
main()
函数 - 处理内存、控制流、异常等
- 处理
typeid
,dynamic_cast
等操作,借助编译器生成的类型信息表工作
关键词:运行时错误(如除以零、空指针访问)✍️ 我的思考:
经典错误:数组越界/指针越界以及错误的折构等等
🧹 5. 程序终止阶段(Termination / Destruction)
时机:main()
退出或程序结束时
任务:
- 调用
atexit()
注册的函数 - 析构全局和静态对象
- 回收资源(内存、文件等)
✍️ 我的思考:
`atexit()`主要在IO操作里面使用
非new的内存存在栈中,在这个时候被按顺序回收
`atexit()`主要在IO操作里面使用
非new的内存存在栈中,在这个时候被按顺序回收
✅ 整体流程图
1 | 源码.cpp |
❗ 各阶段常见错误类型对照表
阶段 | 错误类型 | 示例 |
---|---|---|
预处理 | 宏未定义、文件缺失 | #include "xxx.h" 找不到文件 |
编译 | 语法/类型错误 | int x = "abc"; |
链接 | 符号未定义等 | undefined reference to 'foo' |
运行时 | 空指针、除零等 | segmentation fault , abort() |
RTTI | 类型转换失败 | dynamic_cast 抛出 std::bad_cast |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 羊习习在学习!