编译预处理等细节

       敲过代码的人都知道,任何一个程序都需要经过编译和链接才能生成可执行程序,才能使代码运行起来,那么编译和链接的过程中,编译器做了哪些事,使得程序可以执行呢?

        程序的翻译环境和执行环境

国际c标准中规定,任何一种实现,都存在两个不同的环境。

1:翻译环境:将代码翻译成机器指令;

2:执行环境:用来实际执行代码;

而我们重点需要了解的是翻译环境究竟干了什么,生成了可执行的程序。

翻译环境

 

                                                     图1.编译文件

 如图1.编译文件所示,每一个源文件都会经过编译器生成对应的目标文件,然后经过链接器在链接库中查找对应的头文件,最后生成可执行文件,这里的源文件可以不止一个.c文件,可能有多个.c文件。

 实际操作上我们可以看到,文件编译运行后,会生成对应的.exe文件,如下:

 

我们可以看到,当我们没有编译运行的时候,项目文件夹中只有.c源文件,而当我们编译运行后,就能发现文件夹中多了一个Debug文件夹,里面有编译文件的.exe文件;

图2

图3

  

图4

每一个源文件在编译过程中都会经过编译器生成对应的目标文件,如图三中的test.c就生成了对应的test.obj文件,而每个目标文件(同一个项目中的)都会经过链接器捆绑在一起形成单一而完整的可执行程序,正如图4的test_8_3.exe(项目生成的可执行程序),连接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

当然这只是笼统的讲法,实际上编译和链接可以分为四步。

图5.编译链接具体操作

将编译与链接拆开后,实际上分为以上四个步骤,而每一个步骤都会生成不同的文件;

但是VS是集成开发环境,不好观察细节,于是我们只能在Linux上观察细节。

比如我们在Linux上写上如下代码:

 

预处理细节

当我们想观察预处理后会发生什么事,我们应该这样输入

gcc - E test.c - o test.i
这样编译器就会在经过预处理之后停下来,并且将所有生成的东西放在一个名为test.i的文件中去,当我们打test.i文件后,我们发现。test.i文件之中不仅有我们所写的代码,在原本#include<stdio.h>的位置出现了很多代码,如图6
图6 test.i

而出现的这些代码实际上就是头文件stdio.h中所有的函数。

当然,预处理并不只是只是这样,它还会将所有注释删除,并且将#define符号转换成对应的内容。就比如我#define MAX 10000;那么当我在define下面使用MAX的时候,所有MAX会被替换成10000,当我#define sum  3+2;那么下面使用sum的时候,所有sum被被替换成3+2;然后编译器会将#define那一行给删除掉(因为已经替换完成了)。

编译细节

 想看到编译细节我们应该输入gcc -S test.c

这样编译器会生成一个名为test.s的文件。

打开test.s文件我们可以发现,里面全部是汇编代码,在编译阶段,编译器会进行语法分析,词法分析,语义分析,符号汇总等操作,其中最重要的是符号汇总。

符号汇总

符号汇总会将全局变量,定义的函数(包括main函数) 进行汇总;

接下来就进行汇编阶段;

汇编细节

之后再输入gcc -c test.c

生成了test.o文件  

这个文件是二进制存储的,内部东西都是二进制的东西;

于是我们便明白了汇编会将汇编代码转换为二进制代码

如图7

 

图7

汇编中有一个操作是形成符号表;

这个其实和编译中的符号汇总是相关联的,我们test.c经过编译生成对应的test.s文件后,编译器会将对应的符号汇总,然后在汇编环节,生成一个符号表,在这个环节,编译器会给符号表中的每一个符号一个地址,用来访问,就比如这里的test.c文件经过汇编后,会给Add,g_val,main三个符号一个地址,当然,如果Add函数或者g_val变量是从外部文件声明的话,符号表会将给它们一个无效地址,之后在链接阶段使用符号表

链接细节

 当所有源文件按照elf格式生成了对应的.o文件后,编译器会给每一个.o文件按照elf格式生成一个段表,段表每一个空位存储对应的地址,然后在链接阶段会进行段表合并,它会将相同名称的符号合并,取符号表中的有效地址放在对应的符号表中这个符号表就是决定之后可执行程序能否使用这个函数,若是符号表中存的是无效地址,就无法使用该函数,导致链接错误。

若是在这一系列操作没有发生错误,就成功生成了一个可执行程序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值