C编译链接——从代码到可执行程序的蜕变之路

       计算机其实是比较笨的,它能够识别的只有01序列。也许有人说,不会啊,明明计算机可以帮助我们做很多事情的呀。
       这就要说到计算机行业必不可少的职业:程序员。
       其实我们通常使用计算机来浏览网页、玩游戏等等功能的实现,都离不开程序员手里的代码。举个例子,比如很多人都玩过的电脑小游戏——扫雷。对于计算机使用者来说,只需要打开界面,就可以开始游戏了。而对于程序员来说,首先要分析这个游戏的逻辑关系以及它的玩法,分析清楚之后,使用编译器来编写实现这个游戏的代码,然后在编译器环境下,编译链接生成可执行程序——也就是计算机能够识别的01序列,再经过后期界面设计等环节,扫雷游戏就可以投入使用了。那么,从源代码到计算机能够识别的01序列阶段,到底发生了什么样的进化呢?
       我们上课时,常常听到老师讲“我们以前学习编代码的时候,都是在纸带打点,根据有点没点来判断是0是1 ”。随着计算机的更新换代,也有老师说“我们当时学习编代码的时候,都是汇编指令操作”。到现在,我们使用的都是C语言等高级语言,所谓高级语言,就是计算机替我们做了从源代码到01序列的转化。
       下面我们来看一下详细的转化过程:
       1. 预处理。即预编译,在预处理过程中,主要实现宏替换、去注释、头文件展开以及条件编译的功能。
       我们先来看一个简单的C代码:

#include
#define DEBUG 
#define M 5
int main()
{
	int num = 5;
#ifdef DEBUG    //条件编译
	printf("%d  %d\n", M, num);
#endif
	return 0;
}

       这个代码中,是头文件,其次,定义了两个宏,其中宏DEBUG没有赋值。接着,#ifdef是条件编译,以#endif结尾,意思是,如果宏DEBUG被定义,那么打印printf函数里的内容。//是注释。我们来在CenTOS环境下看一下预编译后的程序:

                                    C编译链接——从代码到可执行程序的蜕变之路_第1张图片


       在这里,大家可以看到,与源代码相比,头文件被展开;由于宏DEBUG被定义,所以条件编译执行;而代码中的宏M被5所替换,这就是宏替换;原先在代码中的注释也被去掉了。
       2. 编译。 源代码经过解析,判断它的语句的意思。在这个阶段产生绝⼤多数的错
误和警告
。然后程序被翻译成汇编代码,再到目标代码(这里目标代码显示为16进制)。下面是汇编代码和目标代码:

          C编译链接——从代码到可执行程序的蜕变之路_第2张图片                                    
                               C编译链接——从代码到可执行程序的蜕变之路_第3张图片                
 

     3. 链接。 每个目标文件代码经过链接 捆绑在⼀起,形成⼀个单⼀而完整的
可执行程序。同时,链接器也会引入标准
C 函数库中任何被该程序所用到的函数,而且它
可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。在这个程序中,只有一个目标文件,C函数库中要用到的函数是printf。

                 C编译链接——从代码到可执行程序的蜕变之路_第4张图片


              上图就是链接后生成的完整可执行程序的16进制序列。
       上面三个过程我们一般统称为源代码的翻译过程。但是我们编写代码的目的并不是要翻译他,而是要让他来达到我们使用的目的,那么就需要运行的结果,所以 除此之后,还有一个执行过程。
       执行过程大致分为以下四步:
       1. 程序载入内存。在冯诺依曼体系结构当中,计算机被分为五大构件,即输入、输出,存储器、运算器和控制器,其中运算器和控制器是CPU的核心部件。各部件运作关系如下图:
C编译链接——从代码到可执行程序的蜕变之路_第5张图片
       大家可以看到,输入单元写入内容后必然要先载入内存,然后传到CPU,经过CPU处理后再次传入内存进行输出,也就是说,内存是连接输入输出的一个桥梁。 我们使用的编译器相当于输入单元,那么同样的,代码必须先载入内存,才能进行处理运算输出。 在有操作系统的环境中:⼀般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
       2. 程序开始执行。接着便调用 main 函数。
       3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈( stack ),存储函数的局部变量和返回地址。程序同时也可以使用静态( static )内存,存储于静态内存中的变量在程序的整个执行过程⼀直保留他们的值。
       4. 终止程序。正常终止 main 函数;也有可能是意外终止。



       如有错误,欢迎指出,一起交流学习!

你可能感兴趣的:(C)