(C)++ 从源代码到可执行文件过程
- 预处理阶段:处理源代码中以 '#' 开头的预编译指令,生成 预编译(.i) 文件。
- 展开宏定义,将 (define) 删除
- 处理条件编译语句
- 处理 (include) 编译指令
- 删除注释
- 编译阶段:将预编译文件转化成相应的汇编码,生成 汇编(.s) 文件。
- 词法分析:从左到右将字符一个个的读入程序,分割成一个个记号
- 语法分析:将产生的记号进行语法分析,构成出语法树,此时很多运算符的优先级和含义就已经确定了。
- 语义分析:检查该语句是否有意义。比如两个指针相乘、是否有除 (0) 操作。
- 汇编阶段:将汇编文件转化成机械码,生成 可重定位目标(.o) 文件。
- 链接阶段:将多个目标文件及所需要的库链接成最终的 可执行(.out/.exe) 文件。
- 静态链接:在生成可执行文件的时候,把所有需要的函数的二进制代码都包含到可执行文件中去。
- 动态链接:在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去执行内存中已经加载的可执行代码,最终达到运行时连接的目的。
(include) 引用头文件顺序 以及 尖括号和双引号区别
如果在 (main.h) 中引用了 (head.h) 中的定义,而不想在 (main.h) 中引用 (head.h),那么可以在 (main.cpp) 里面先引用 (head.h),在引用 (main.h) 即可。
// head.h 内容
struct Node {
int a, b;
};
// main.h 内容
Node node
/// main.cpp 错误写法
#include "main.h"
#include "head.h"
int main() {
return 0;
}
// main.cpp 正确写法
#include "head.h"
#include "main.h"
int main() {
return 0;
}
使用尖括号和双引号的区别在于:编译器预处理阶段寻找头文件的路径顺序不一样。
- 使用双引号的查找顺序
- 当前头文件目录
- 编译器设置的头文件路径
- 系统变量指定的头文件路径
- 使用尖括号的查找顺序
- 编译器设置的头文件路径
- 系统变量指定的头文件路径
内存泄漏
内存泄漏是由于疏忽或错误操作使程序未能释放掉不再使用的内存的情况,并不是物理上的消失,而是程序分配某段内存后,由于设计错误,失去了对该段内存的控制,从而造成内存的浪费。
内存泄漏的情况:
- 程序通过 (malloc、new) 等内存申请操作后,在用完后没有调用 (free、delete) 删除掉,那么此后这段内存不会再被使用,造成内存泄漏。
- 程序使用系统分配的资源而没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低。
- 基类析构函数没有定义成虚函数,那么子类的析构函数不会被调用,子类的内存没有被释放,造成内存泄漏。
内存溢出
程序申请内存的时候,超出了系统实际分配给你的空间,此时系统无法完成满足你的需求,就会发生内存溢出。
内存溢出的情况:
- 内存中加载的数据量过于庞大。
- 代码中存在死循环或者递归过深导致栈溢出。
- 内存泄漏导致内存溢出。
段错误
段错误就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统保护的。
段错误的原因:
- 使用了野指针
- 试图对只读空间进行写操作
- 访问了不存在或系统保护的内存空间