一,概述

对于编译,各个平台和编译器的编译都是存在差异的,但是总的来说,对个全部的编译器,其过程都是大致相同的。

关于对编译原理的理解,建议有兴趣去找专门的书籍研究,所以此博客只是讲一下大概的过程。建议在研究编译原来的时候,在Linux的平台下,使用GCC去研究,因为在linux的下,通过外壳命令,可以细化编译的过程,一步一步的进行。

编译就是利用编译程序(一般是IDE或者CMD)将使用源语言编写的源程序产生目标程序的过程。编译是将源代码转化为目标程序的过程。简单的理解其实,编译就是把高级语言编程计算机或者嵌入式系统可以直接识别的二进制代码。

一个源代码到目标代码的过程一般经过以下几个过程:

⑴编译预处理;

⑵编译;

⑶        汇编;

⑷        链接;

 

二、编译预处理

预处理主要包括以下几个功能:

⑴        处理代码成的注释:注释(//或者/**/)全部使用空格进行替换;

⑵        处理头文件的包含:展开#include包含的头文件.

一般在定义头文件的时候,都会使用条件编译来进行宏声明,比如

#ifndef_STATICLINKLIST_H_

#define_STATICLINKLIST_H_

…….

#endif

不管是在工程中,还是在学习中,都应该使用#ifndef…#define…#endif的形式,这样做的目的是为了防止各个文件的重复包含,造成在预编译的时候出错。按照以上的形式增加了条件编译,在重复包含的时候,就会跳过,不会出现错误。

⑶        处理宏定义:将所有的#define删除,并且展开所有的宏定义;

⑷        条件编译的处理:#if…#else…#endif等条件编译进行处理。注意此部分不是直接删除,还包含了其他的动作。

⑸        保留#pragma指令:其他以#开拓的都是预编译指令,但是这个指令例外,此为编译器指示字,所以此步骤需要保留,关于此指示字的具体用法,在后面的内容将会详细讲解。

 

预处理的过程,是由.c文件到.i文件的过程。在linux的gcc环境中使用以下指令来进行单步操作:

         gcc –E file.c –o file.i  注意:file为文件的名字

 

三、编译

由于早期只有汇编语言,所以很多高级编程语言在转化为目标文件之前,会先将源代码文件转换为汇编代码,在通过汇编表去生成二进制的可执行代码文件。编译器就是将预编译生成的.i文件,继续加工为汇编文件。

编译的过程注意分为以下几个部分:

⑴   词法分析:主要是分析关键字、标识符等是否合法。比如说变量名和关键字的名字重复就是有这个步骤来发现的,当然现在很多IDE的编译器在编辑的过程中就能检测出这一类的错误。

⑵   语法分析:主要是分析控制结构,表达式等是否遵循语法规则。

⑶   语义分析:在语法分析的基础上进一步分析表达式的合法性等。

在linux的gcc环境中使用以下指令来进行单步操作:

gcc –S file.c –o file.s

 

四、汇编

汇编的步骤就是将编译生成的汇编代码进一步转换为二进制代码。

一般情况下,每一个汇编语句都会对应一条机器指令。

当然在有进行汇编的过程之后,的可执行代码还是不能直接执行的。

在linux的gcc环境中使用以下指令来进行单步操作:

gcc –c file.s –o file.o

 

五、链接

链接的作用在于将各个模块之前相互引用的部分处理好,是各个模块之间能够正常的进行调用和跳转。简单点说,就是在对各个模块进行编译之后,进行融合的过程。

链接有分为静态链接和动态链接。

静态链接:在生成可执行文件的时候,将所有的需要使用的函数的二进制代码都添加到目标文件中。所以链接器需要知道参与链接的目标文件所需要的函数,也需要知道目标文件能够提供哪些函数,即需要知道需求和资源才能进行正确的链接。如果缺少其中的一起,在链接的过程中就会报错。这些信息有符号表和重定位表来提供的。这样做的优势在于可执行文件在运行的过程中,不需要依赖于其他的库文件,也不需要将库文件和可执行文件一起发布。但是这样的做法也会造成发布的可执行程序比较大,同时在库存在更新的时候,必须要再次发布可执行文件。

动态链接:动态链接和静态链接刚好相反,在编译的时候不需要将库中的相关函数和接口添加到可执行文件中,而是通过记录一些列的符号和参数,在程序运行或者加载的时候将这些信息传递给操作系统,由操作系统来负责库的加载,提供给可执行文件使用。这样的做法的优势可以实现程序之间的代码共享,但是在运行的过程中需要加载库,导致程序的效率较低。

在linux下编译的时候,需要增加-static来声明是静态链接。对于静态链接和动态链接没有谁好谁坏之分,一般是根据场合来进行选择的,但是在嵌入式工程中,基本上都是使用静态链接。

 

六、生成目标文件(可执行文件)

目标代码的生成是最后一个阶段。目标代码的生成阶段一般需要考虑以下三个问题:

⑴        目标代码尽可能的短,即占用空间小;

⑵利用MCU的寄存器,减少代码访问存储单元的次数,即时间最少;

⑶利用MCU的指令系统,提高代码质量。

 

 

可执行代码的文件后缀一般和编译器有关系的。其实现在嵌入式开发大部分都是IDE的编译环境了,属于以上所有的东西都是由编译器厂商考虑的。但是我们也需要了解整个过程,编译在开发中排查问题,还有合理的利用编译器的特点,写出高质量的代码。