目录
一、概述:翻译环境、执行环境
1.翻译环境
2.执行环境(运行环境)
二、详述翻译环境——编译环境、链接环境
1.编译环境
2.链接环境
三、详述编译过程——预编译、编译、汇编
1.预编译(预处理)
2.编译(Linux环境下测试)
编译器如何将C语言代码翻译成汇编代码?
(1)词法分析
(2)语法分析
(3)语义分析
(4)符号汇总
3.汇编(Linux环境下测试)
汇编器如何将汇编代码翻译成二进制指令?
(1)生成符号表
四、详述链接过程——合并段表、符号表的合并与重定位
1.合并段表
2.符号表的合并与重定位
一、概述:翻译环境、执行环境
在ANSI C(标准C语言)的任何一种实现中,都存在两种环境:
1.翻译环境
在这个环境中,源代码被转换为可执行的二进制指令
在一个项目工程中,test.c就是源文件(源代码),可以在存放该项目工程的文件夹中找到该源文件。该源文件存放的是文本信息代码,可以直接用记事本打开,可以看到源代码
这样的源代码并不能直接运行,而是要通过翻译环境形成一个可执行程序text.exe,该可执行程序中存放的是二进制指令(机器指令)。同样也可以用记事本打开,但是打开后看到的是一堆乱码,因为存放的都是二进制的信息,记事本打开无法查看。
2.执行环境(运行环境)
用于实际执行代码(执行二进制指令)
当源文件test.c通过翻译环境产生可执行文件test.exe后,该可执行文件就可以通过执行环境执行,最终得到执行结果。
二、详述翻译环境——编译环境、链接环境
在翻译环境下,又分为两个环境:编译环境和链接环境
1.编译环境
源文件test.c通过编译器生成目标文件test.obj(Linux环境下的目标文件时test.o),该目标文件中存放的是二进制指令,用记事本打开同样是一堆乱码。
(VS2022环境下的编译器是cl.exe)
如果有多个源文件,那么这些源文件都要单独通过编译器生成对应的目标文件
2.链接环境
所有目标文件和链接库经过链接器的处理最终生成一个可执行文件test.exe
(VS2022环境下的链接器是link.exe)
链接库:是Windows系统中封装代码和数据以及实现资源共享的一种方式,本质上是已经编译好的二进制指令文件(机器指令文件)
例如包含头文件时使用的C语言标准库就是以链接库的形式和其他目标文件通过链接器链接
三、详述编译过程——预编译、编译、汇编
编译器对源文件进行编译的具体过程分为3步:预编译(预处理)、编译、汇编
1.预编译(预处理)
预编译时,编译器会进行以下动作:
1.将所有的注释替换为空格
2.将#define定义的标识符(宏)替换为对应的常量或函数(宏展开)
3.将包含的头文件中所有的内容以及源文件代码整合并生成一个test.i文件
(预处理后得到的test.i文件仍然是C语言代码)
VS2022环境下,将编译器设置改为预处理到文件,这样编译器会将预处理完成后的结果放到test.i文件。相比于源文件,该test.i文件末尾代码是进行了注释替换和宏展开的源代码,上面还有一万多行代码是将头文件所包含的所有内容。
2.编译(Linux环境下测试)
将test.i文件的C语言代码翻译成汇编代码,存放到test.s文件中
编译器如何将C语言代码翻译成汇编代码?
(1)词法分析
源代码程序被输入扫描器,扫描器的任务就是进行简单的词法分析。将代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)。
例如下面代码,代码中的字符可被分割成一系列的记号:
(2)语法分析
语法分析器对扫描器扫描产生的记号进行语法分析,从而产生语法树。这些语法树是以表达式为节点的树。
(3)语义分析
主要是检查是否结构正确的句子所表示的意思也合法、执行规定的语义动作(如:表达式求值、符号表填写、中间代码生成等)
(4)符号汇总
将所有源文件的全局符号汇总起来,包括全局变量名、全局函数名、main。(因为只有全局的函数、变量才涉及到跨文件使用)
3.汇编(Linux环境下测试)
将test.s汇编代码通过汇编器翻译为二进制指令,存放到test.o目标文件中(Windows环境下的目标文件为test.obj)
汇编器如何将汇编代码翻译成二进制指令?
(1)生成符号表
每个源文件都有自己的符号,汇编器会将源文件各自的符号列成一个表,并为每个符号给予地址。
例如,在下列项目中,有两个源文件 add.c 和 test.c ,汇编器在各自目标文件(.obj)中文件中生成符号表
add.obj
test.obj
四、详述链接过程——合并段表、符号表的合并与重定位
1.合并段表
在gcc编译器(Linux环境)中,所有生成的目标文件和二进制文件都是按照 elf 文件格式组织的。将所有的目标文件或二进制文件分成不同的段,每个段存放不同的数据。
合并段表就是将这些目标文件和二进制文件相同的段进行合并,生成二进制可执行文件
2.符号表的合并与重定位
每个目标文件都有自己的符号表,需要将这些符号表进行合并
最终去查找函数的时候,只需要通过这个合并的符号表根据各符号的地址去查找即可
正是因为符号表合并的存在,才可以进行函数等的跨文件调用。
如果程序运行时出现了未定义的外部符号报错,说明源文件中并未定义该符号。根据地址查找该符号时,找不到该符号的定义。