一、编译过程

语雀内容

  • 预处理
    • 处理以#开头的指令
  • 编译、优化
    • 将源码编译成汇编代码(.cpp文件 变成.s文件)
  • 汇编
    • 汇编代码编译成机器代码(.s文件 变成.o文件),.o文件也叫目标文件。
  • 链接

    • 将所有的目标文件链接成一个整体(可执行程序)。因为不同目标文件之间可能存在函数引用。

      二、两种链接

  • 静态链接

    • 代码从其所在的静态链接库拷贝到最终的可执行程序中,在该程序被执行时,代码会被装入到该进程的虚拟地址空间中。
    • 简单理解:库代码全部拷贝到可执行程序中。
    • 优点:快。
    • 缺点:链接库的改动,整个都要重新编译链接。牵一发而动全身。更新十分麻烦。
  • 动态链接

    • 代码被放到动态链接库或共享对象的某个目标文件中。链接程序只在最终的可执行程序中记录了共享对象的名字等。程序执行时,将动态链接库的全部内容会被映射到运行时相应进行的虚拟地址空间。
    • 简单理解:可执行程序中记录了代码位置,执行的时候再把相应代码载入。
    • 优点:灵活,更新方便。
    • 缺点:慢,每次执行相应代码都需要进行链接。

      三、头文件

  • 在有头文件之前

    • 事实上,只有.cpp文件可以编译执行,但是在是多文件的编译中,如果文件A的类class,在文件BCD中都使用到,那就必须要在BCD中都有class的定义,如果class要修改,所有地方都要修改了。
  • 头文件解决多处定义
    • 通过在头文件H中定义class,然后在ABCD中通过#include这样的预处理命令,自动化地把class导入,就能比较好的解决前面的问题了。
    • 因此,头文件 + #include,完成了自动化导入。

头文件保护

头文件可以被多次导入,但是class只需被定义一次,然后多方使用。这里就需要头文件保护符类来使编译器只会定义class一次。

  1. // 下面这称为include防范,宏防范,或者叫头文件保护符。
  2. // 避免重复包含。
  3. #ifndef _FUCK_H_ // 当且仅当变量未定义时为真,#ifdef相反,类似if条件判断。
  4. #define _FUCK_H_ // 把名字设为预处理变量,必须保证唯一
  5. class Fuck {}
  6. #endif
  7. // ****************************************
  8. // 上下的作用相同,显然下面的要更简单。
  9. // ****************************************
  10. // 注意,#pragma once并不是C/C++标准,但是一个广泛被支持的前置处理符号
  11. #pragma once
  12. class Fuck{}

四、分离式编译

  1. // fact.h
  2. int fact(int); // 函数声明
  3. // fact.cc
  4. #include "fact.h"
  5. int fact(int a){ // 函数定义
  6. return a;
  7. }
  8. // main.cc // main函数文件
  9. #include "fact.h"
  10. int main(){
  11. fact(1);
  12. }
  1. # 编译以上程序
  2. $ CC -c factMain.cc fact.cc # 生成main或a.out可执行文件
  3. $ CC -c factMain.cc fact.cc -o main # 生成main或者main.exe执行文件
  4. # 分离式编译:逐个编译文件,再链接文件
  5. $ CC -c factMain.cc # 编译生成factMain.o目标文件
  6. $ CC -c fact.cc # 编译生成fact.o目标文件
  7. $ CC factMain.cc fact.cc -o main # 链接生成main或者main.exe可执行程序