一、编译过程
- 预处理
- 处理以#开头的指令
- 编译、优化
- 将源码编译成汇编代码(.cpp文件 变成.s文件)
- 汇编
- 汇编代码编译成机器代码(.s文件 变成.o文件),.o文件也叫目标文件。
链接
静态链接
- 代码从其所在的静态链接库拷贝到最终的可执行程序中,在该程序被执行时,代码会被装入到该进程的虚拟地址空间中。
- 简单理解:库代码全部拷贝到可执行程序中。
- 优点:快。
- 缺点:链接库的改动,整个都要重新编译链接。牵一发而动全身。更新十分麻烦。
动态链接
在有头文件之前
- 事实上,只有.cpp文件可以编译执行,但是在是多文件的编译中,如果文件A的类class,在文件BCD中都使用到,那就必须要在BCD中都有class的定义,如果class要修改,所有地方都要修改了。
- 头文件解决多处定义
- 通过在头文件H中定义class,然后在ABCD中通过#include这样的预处理命令,自动化地把class导入,就能比较好的解决前面的问题了。
- 因此,头文件 + #include,完成了自动化导入。
头文件保护
头文件可以被多次导入,但是class只需被定义一次,然后多方使用。这里就需要头文件保护符类来使编译器只会定义class一次。
// 下面这称为include防范,宏防范,或者叫头文件保护符。
// 避免重复包含。
#ifndef _FUCK_H_ // 当且仅当变量未定义时为真,#ifdef相反,类似if条件判断。
#define _FUCK_H_ // 把名字设为预处理变量,必须保证唯一
class Fuck {}
#endif
// ****************************************
// 上下的作用相同,显然下面的要更简单。
// ****************************************
// 注意,#pragma once并不是C/C++标准,但是一个广泛被支持的前置处理符号
#pragma once
class Fuck{}
四、分离式编译
// fact.h
int fact(int); // 函数声明
// fact.cc
#include "fact.h"
int fact(int a){ // 函数定义
return a;
}
// main.cc // main函数文件
#include "fact.h"
int main(){
fact(1);
}
# 编译以上程序
$ CC -c factMain.cc fact.cc # 生成main或a.out可执行文件
$ CC -c factMain.cc fact.cc -o main # 生成main或者main.exe执行文件
# 分离式编译:逐个编译文件,再链接文件
$ CC -c factMain.cc # 编译生成factMain.o目标文件
$ CC -c fact.cc # 编译生成fact.o目标文件
$ CC factMain.cc fact.cc -o main # 链接生成main或者main.exe可执行程序