读书-程序员的自我修养-链接、封装与库(5: 第二章:静态链接 预处理,编译,汇编,链接)
Hello World执行的四个步骤
#include <stdio.h>
int main()
{
printf("Hello world\n"):
return 0;
}
使用GCC编译器:
gcc hello.c
./a.out
上述过程可以分解为四个步骤:
1. 预处理(prepressing) gcc -E hello.c 生成 xx.i文件
2. 编译(compilation) gcc -S xx.i 生成 xx.s
3. 汇编(Assembly) gcc -c xx.s 生成 xx.o
4. 链接(Linking) gcc xx.o 生成 a.out
1. 预处理的工作内容
1.1 预编译作用
gcc -E hello.c -o hello.i
预编译过程主要处理那些源文件中以“#”开始的预编译指令
比如:#include #define #if #ifdef
编译器就是将高级语言翻译成机器语言的一个工具。
1.2 预编译具体工作
1. 将所有的#define 删除,并且展开所有的宏定义
2. 处理所有条件编译指令,如#if #ifdef #elif #else #endif
3. 处理#include预编译指令,将包含的文件插入到该预编译指令的位置,这个过程是递归进行的。
4. 删除所有的注释 // 和 /**/
5. 添加行号和文件名标识,比如 #2 hello.c 2 ,以便调试信息,错误,告警等显示行号
6. 保留所有的#pragma等编译器指令,因为编译器需要他们
结果:经过预编译后的hello.i文件不包含任何宏定义,因为所有的宏已经被展开。
2. 编译的工作内容
gcc -S hello.i -o hello.s
编译过程:编译过程一般可以分为6步:扫描、词法分析,语法分析,语义分析、源代码优化、代码生成、目标代码优化。
1. 扫描:首先源代码被输入到扫描器(Scanner)
2. 词法分析:将源代码的字符序列分割成一系列的记号
记号分为: 关键字,标识符,字面量(数字字符串),特殊符号
3. 语法分析:对记号进行语法分析,产生语法树(Syntax Tree),就是以表达式为节点的树
4. 语义分析:给语法树的表达式标识类型
5. 中间语言生成:简单的代码优化,如 4+5 直接计算结果
6. 目标代码生成与优化:选择合适的寻址方式、使用位移来代替乘法运算、删除多余的指令等
后面将图贴上来!
3. 汇编的工作内容
gcc -c hello.s -o hello.o
汇编:将汇编代码转变成机器可以执行的代码指令,每一个汇编语句几乎都对应一条机器指令。
4. 连接器的工作内容
4.1 重定位、符号
重定位(Relocation):重新计算各个目标的地址过程叫做重定位(Relocation)
符号(Symbol):表示一个地址,这个地址可以是一段子程序/函数,也可以是一个变量的起始地址。
4.2 模块调用
模块之间的调用归结为如何通信的问题,常见是模块间的函数调用和模块间的变量访问。
4.3 链接
- 模块的拼接的过程就是链接
- 链接(Linking):
主要内容就是把各个模块之间的相互引用的部分都处理好,使得各个模块之间能够正确的衔接。 - 链接的主要过程:包括地址和空间分配,符号决议,重定位
4.4 运行时库
库:其实是一组目标文件的包,就是一些最常用的代码编译成的目标文件后打包存放。
运行时库(Running Library):它是支持程序运行的基本函数的集合