下面我们通过一个简单的程序进入
#include
#define PI 3.14
int main()
{
printf("hello world\n"); // hello world
printf("%f", PI);
return 0;
}
在Linux使用GCC来编译此程序
gcc hello.c -o
./a.out
以上其实经历了四个步骤
gcc -E hello.c -o hello.i //预处理
gcc -S hello.i -o hello.s //编译
gcc -c hello.s -o hello.o //汇编
gcc hello.o -o hello //链接
预处理图片来自[程序喵大人]gcc a.c 究竟经历了什么?)
其实预处理主要 *** 作有这几个:
- 将所有的#define删除,并展开所有的宏定义
- 删除程序中所有的注释
//
/**/
- 处理所有的条件编译,#if、#ifdef、#elif等
- 处理所有的#include指令,把这些头文件的内容都复制到引用的源文件中
- 添加行号和文件名标识,方便编译器产生警告及调试信息
- 保留所有的#pragma编译器指令,因为编译器会使用他们
我们可以查看经过预处理生成的.i文件
发现PI已经被替换成了3.14,而上面是展开的头文件内容(许多行)
经过预编译后的.i
文件不包含任何宏定义,因为所有的宏已展开,并且包含的文件也已经被插入到.i
文件中,我们可以通过查看预编译后的文件来确定宏展开的问题。
gcc -S hello.i -o hello.s //编译
编译过程就是把预处理完的文件进行一系列 *** 作产生相应的汇编代码文件
可以看到hello.s文件里面都是汇编语言
下面讲解编译器的 *** 作,先举一个例子(图片来自[程序喵大人]gcc a.c 究竟经历了什么?)
array[index] = (index + 4) * (2 + 6);
- 词法分析:扫描器简单的将源代码的字符序列分割成一系列的记号,记号一般分为关键字(typename)、标识符(变量)、字面量(数字、字符串)和特殊符号(+、=)
- 语法分析:对记号进行语法分析,产生
语法树(以表达式为节点的树)
,如果出现表达式不合法,比如括号不匹配、表达式缺少 *** 作等,编译器会报语法分析阶段的错 - 语义分析:语法分析仅完成了对表达式的分析,但不知道表达式是否具有意义,比如C语言中给两个指针做乘法运算无意义。编译器所能分析的语义为静态语义,通常包括声明和类型的匹配,类型的转换。比如将一个浮点数赋值给一个指针,语义分析程序会发现类型不匹配,编译器报错。动态语义一般指运行期出现的语义相关问题,比如将0作为除数是一个运行期错误
- 中间语言生成:上述例子会将
2 + 6
直接替换得到8,然后运算,这是对语法树的优化。 - 目标代码生成与优化:最后将目标代码生成汇编代码
gcc -c hello.s -o hello.o //汇编
汇编器将汇编代码转变成机器可以执行的指令,每一个汇编语句都对应一条机器指令。
汇编器只是根据汇编指令和机器指令的对照表一一翻译即可
链接可以看到vscode显示hello.o文件是二进制文件,里面都是010101这些只有机器才看得懂的指令
gcc hello.o -o hello //链接
人们把每个源代码独立的编译,然后按照需要将它们组装起来,这个组装模块的过程就是链接
链接的工作就是把一些指令对其他符号地址的引用加以修正,链接过程主要包括了地址和空间分配,符号决议和重定位等步骤
静态链接
每个模块的源代码文件经过编译器编译成目标文件(.o/.obj),目标文件和库一起链接形成最终可执行文件。而最常见的就是运行时库,它是支持程序运行的基本函数的集合。库其实是一组目标文件的包,就是一些最常用的代码编译成目标文件后打包存放。
例子
如果我们在模块main.c中要使用func.c里的foo函数,那么我们在main.c的每一处调用foo的时候都需要知道它的地址。但在编译器编译main.c的时候它不知道foo的地址(单独编译),它暂时把这些调用foo的指令的目标地址搁置,等待最后链接的时候由链接器去将这些指令的目标地址修正。链接器根据引用的符号foo,自动取对应的fun.c模块查找foo的地址,然后将main.c模块中所有引用到foo的指令重新修正,让它们的目标地址为真正的foo函数的地址。这就是静态链接的最基本的过程和作用。
如果我们不用链接器,我们就要手动的补充foo函数的地址,但是文件都是单独编译的,有可能fun.c文件编译后目标地址发生改变,那么所有引用该foo函数的地方我们都要一一修改,这是所不能忍受的,还是交给链接器去做比较舒服。
参考-
《程序员的自我修养——链接、装载与库》
-
[程序喵大人]gcc a.c 究竟经历了什么?
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)