详解C语言的编译与链接 |
您所在的位置:网站首页 › c语言编译器的代码怎么写的 › 详解C语言的编译与链接 |
文章目录
写在前面1.程序的翻译环境和执行环境2.详解编译+链接2.1翻译环境2.2编译的几个阶段2.2.1 预编译2.2.2 编译2.2.3 汇编2.2.4 链接
2.3 运行环境
3. 总结
写在前面
众所周知,我们写的代码是放在一个或多个xxx.c的文件,你知道从源代码到可执行文件经历了哪些过程吗?或许这个问题一直困扰着你,那就跟着我,让我们一起来探寻其中的奥秘吧! 本文用到的编译器: 1.vs2013(windows下面) 2.gcc编译器(linux下面) 1.程序的翻译环境和执行环境在ANSI C的任何一种实现中,存在两个不同的环境。 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境,它用于实际执行代码。 2.详解编译+链接 2.1翻译环境一个程序可能会有多个xxx.c的源文件,每个源文件都会单独经过编译器的编译生成一个xxx.obj的目标文件(window系统下)。 每个目标文件由链接器(linker)捆绑在一起,链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。形成一个单一而完整的可执行程序。 一个xxx.c文件,经过编译链接以后,最终变成可执行的xxx.exe文件,经过了如下几个过程: 由于vs是集成开发环境,具体的细节没办法很好的观察,接下来我们用linux底下的gcc编译器,来逐一分析各个阶段的究竟做了哪些事情。 2.2.1 预编译(1)我们在编译器里面写如下代码: int main() { int a = 10; int b = 20; int c = a + b; return 0; }紧接着执行gcc test.c -E -o test.i,这句话的意思是预编译test.c以后停下来,并将结果输出到test.i里面去。 执行完gcc test.c -E -o test.i以后,我们再次打开test.i文件看一下: 然后在test.c这个文件包含我们自己定义的头文件test.h,执行完gcc test.c -E -o test.i以后,我们再次打开test.i文件看一下: 我们执行完gcc test.c -E -o test.i以后,打开test.i文件看一下: 我们执行完gcc test.c -E -o test.i以后,打开test.i文件看一下: 我们用gcc test.i -S 编译我们的test.i文件,这句话的意思是编译完成之后就停下来,结果保存在test.s中。结果如下: 我们用gcc -c test.c 编译我们的test.c文件,这句话的意思是汇编完成之后就停下来,结果保存在test.o中。(windows底下生成的是xxx.obj目标文件,linux底下生成的是xxx.o的目标文件)结果如下: 上面我们知道,经过汇编以后会生成xxx.o的目标文件。而在linux系统下,xxx.o的目标文件是elf格式的。它会把一个文件分成几个段,每个段代表着不同的意思。有个工具叫readelf可以看的懂它,因此我们可以用这个工具打开我们的test.o文件来观察一下。我们输入readelf test.o -a看一下它的所有段。 我们gcc -c test.c 编译我们的test.c文件,然后readelf test.o -a来打开这个文件: 通过上面的分析,我们知道,生成的目标文件通过链接器链接在一起,最终生成可执行的程序。而在linux底下生成的xxx.out的可执行程序的格式也是elf的。 所以在这个阶段把它们链接在一起,就是把它们对应的段合并起来。当然它们合并肯定是有一定规则的,最终它们会合并成一个。 程序执行的过程: 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。程序的执行便开始。接着便调用main函数。开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回 地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值。终止程序。正常终止main函数;也有可能是意外终止。 3. 总结下面用一张图来总结一下各个过程做了哪些事情: |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |