通常我们在编译时,会简单的使用一条命令,gcc hello.c -o hello,就会产生一个可执行文件,这样做无可厚非,原因在于gcc把哪些复杂的步骤都屏蔽了,只提供了一个很简单的使用命令。
其实在编译时,主要包含4个步骤,分别是
<1>预处理(preprocess) gcc -E hello.c -o hello.i
<2>编译(compile) gcc -S hello.c -o hello.s
<3>汇编(assembly) gcc -c hello.s -o hello.o
<4> 链接(link) ld hello.o xx.o -o hello | gcc hello.o -o hello
下面分别就四个步骤做简要介绍
第一步:预处理
预处理主要是处理以#开头的部分,比如包含头文件,展开宏定义,条件预处理指令,处理规则如下:
a. 删除所有的define,并展开宏定义
b. 处理条件预处理执行.
c. 把头文件包含进来,注意这个过程是递归进行的,也就是头文件包含的头文件也会被包含进来。
d. 删除注释
e. 添加行号,便于后期调试
f. 保留pregma
第二步:编译
编译是将预处理产生的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件。
词法分析主要是使用基于有线状态机的scanner分析出Token
语法分析对有扫描器产生的Token进行语法分析,从而产生语法树木(采用上下文无关语法分析)
语义分析,编译器能分析的语义是静态语义,静态语义通常包括声明和类型的匹配,类型的转换比如当一个浮点型的表达式赋值给一个整型的表达式时,其中就隐含了一个浮点型到整型转换的额过程,语义分析过程需要完成这个步骤。比如讲一个浮点型赋值给一个指针的时候,语义分析程序就发现这个类型不匹配,编译器会报错。动态语义一般指在运行期出现的语义相关问题,比如讲0作为除数是一个运行期语义错误。
第三步:汇编
汇编是将编译产生的汇编代码文件转变成可执行的机器指令。这一步相对来说比较简单,每个汇编语句都有相对应的机器指令,只需根据汇编执行和机器指令的对照表一一翻译就可以了。
第四步:链接
虽然上一步产生的文件已经是机器指令文件,但是依然无法执行,原因在于某些符号无法解析,比如printf,所以需要链接,链接过程主要完成的是重定位,也就是符号解析,将符号对应的地址替换成正确的地址。例如printf可能会存在于printf.o文件中,在hello.o中,汇编时可以先将printf符号的地址设置为0,等到了链接这一步,在与printf.o链接,从而确定printf符号的位置。
预处理产生.i文件,编译产生汇编文件,汇编产生目标文件(机器指令),链接产生可执行文件。
---------------------
作者:sheldonwong
来源:CSDN
原文:https://blog.csdn.net/sheldonwong/article/details/75216545
版权声明:本文为博主原创文章,转载请附上博文链接!