详解"/>
程序的预编译、编译、汇编、链接详解
程序的预编译、编译、汇编、链接详解
- 程序的翻译环境和执行环境
- (1)翻译环境
- ①编译器的执行过程:
- ②链接器的执行过程:
- (2)运行环境
程序的翻译环境和执行环境
在ANSI C的任何一种实现中,存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境,它用于实际执行代码。
对于程序的翻译环境和执行环境,这里在Linux操作系统下进行讲解。
(1)翻译环境
翻译环境包括编译器和链接器,编译器有预编译,编译,汇编三个操作,链接器是把编译器生成的目标文件和链接库链接生成一个可执行文件。
这里通过在Linux环境下运行text.c文件和add.c文件为例来较为细致的讲述翻译环境
text.c文件
extern int add(int x,int y);int main()
{int a = 10;int b =5;printf("%d\n",add(a,b));return 0;
}
add.c文件
int add(int x,int y)
{int ret = x + y;return ret;
}
①编译器的执行过程:
预编译主要会对源文件进行一系列文本操作,包括头文件的包含,删除注释,#define定义符号的替换,条件编译等。我们执行可以gcc -E text.c指令对程序进行预编译,预编译后的结果会直接输出在屏幕上,但是我们一般执行
gcc -E text.c -o text.i指令将预编译后的结果输出为text.i文件。同理,我们对add.c进行同样的操作得到add.i文件。
预编译完成后,编译器就会对生成的text.i文件和add.i文件进行编译了,将C语言代码编译为汇编代码,编译阶段的主要操作就是语法分析,语义分析,词法分析,符号汇总。 语法分析,语义分析,词法分析顾名思义就是对我们写的代码进行语法,语义,词法方面的分析,这样编译器就知道我们写的代码是个什么意思,有没有语法错误。符号汇总就是指编译器对我们代码中全局的符号进行一个汇总。比如对text.i文件进行符号汇总的结果就是,add和main,对add.i文件进行符号汇总的结果就是add。
这些就是编译器在编译阶段的主要功能,我们只需要执行gcc -S text.i指令就会对text.i文件进行编译了,并且自动生成一个text.s文件,同理,我们对add.i文件进行同样的操作就会得到add.s文件。
编译完成后,编译器就开始对生成的.s文件进行汇编操作,汇编操作会将汇编代码转化为二进制指令,其中的主要操作有形成符号表。
此时编译器对text.s文件形成符号表的结果就是:
add 0x0000(因为并不知道add.c文件在哪里,所以add函数的地址是个默认值)
main 0x0123
对add.s文件形成符号表的结果就是:
add 0x2454
这些就是编译器在汇编阶段的主要功能,我们只需要执行gcc -c text.s指令就会对text.s文件进行汇编操作,自动生成一个text.o文件,同理,对add.s进行同样的操作也会生成一个add.o文件。
历经预编译,编译和汇编操作后,编译器的任务总算是完成了,组成一个程序的每个源文件通过编译器转换成目标文件,后面就交给链接器吧。
②链接器的执行过程:
链接器会将目标文件和链接库链接生成一个可执行文件(.exe),而可执行文件实际上是一个二进制文件,其中包含的是二进制指令/机器指令。
链接器将目标文件和链接库链接在一起生成可执行文件,其中的一些主要操作有合并段表,符号表的合并和重定位。这里我们重点梳理一下符号表的合并和重定位,之前在汇编操作阶段对text.i文件和add.i文件形成了两个符号表:
add 0x0000(因为并不知道add.c文件在哪里,所以add函数的地址是个默认值)
main 0x0123
add 0x2454
我们知道程序的运行是从主函数开始执行的,而在主函数所在文件中我们却无法找到add函数的地址,也就无法正常运行程序,而链接器将目标文件中的符号表进行合并和重定位得到一个新的符号表:
main 0x0123
add 0x2454
这样在最终生成的.exe文件中就能够在符号表中找到其他函数的地址,从而使得程序得以正常运行。
这就是链接器的主要操作,我们可以执行gcc text.o add.o -o text来执行链接操作,最终生成一个text.exe文件。最后由运行环境运行可执行程序,最终产生我们需要的结果。
Linux环境下目标文件是.o文件,windows环境下目标文件是.obj文件
(2)运行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同
时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。 - 终止程序。正常终止main函数;也有可能是意外终止。
更多推荐
程序的预编译、编译、汇编、链接详解
发布评论