0%

从编译器角度理解C++代码的编译和链接原理

编译过程

预编译—>编译—>汇编—>二进制可重定位目标文件

预编译

首先是将源代码相关的头文件,如stdio.h等被预编译器cpp预编译成一个.i文件。

1
gcc -E hello.c -o hello.i

预编译过程主要处理那些在源代码中以 # 开始的预编译指令。如:#include 、 #define ,主要规则如下

  • 将所有的 “#define” 删除,并且展开所有的宏定义
  • 处理所有的条件预编译指令,比如 #if, #ifdef, #elif, #else, #endif
  • 处理 #include 预编译指令,将被包含的文件插入到该预编译指令的位置(注意:这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件)
  • 删除所有的注释 // 和 /**/
  • 添加行号和文件名标识,便于后面编译器调试用的行号信息和错误警告显示行号
  • 保留所有的#pragma编译器指令,因为编译器需要使用它们

经过预编译后.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件已经被插入到.i文件中。所以当我们无法判断宏定义是否正确或头文件是否包含正确时,可以查看预编译后的文件来确定问题。

编译

编译过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生产相应的汇编代码文件,这个过程往往是程序构建过程中的核心部分,也是最复杂的部分

汇编

将汇编代码转变为机器可以执行的指令,每一个汇编指令几乎都对应一条机器指令。所以汇编器的汇编过程相对于编译器来说比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化,只需根据汇编指令和机器指令的对照表一一翻译即可。

1
as hello.s -o hello.o
1
gcc -c hello.s -o hello.o

链接

编译完成的所有.o文件 + 静态库文件

步骤一:所有.o文件段的合并,符号表合并后,进行符号解析

步骤二:符号的重定位(重定向)。

求大佬赏个饭