当我们编译了一个c文件,最终目的是让它能够成为可以执行的代码,就是能够受控或者控制硬件的东西。控制硬件的东西,一般都是二进制代码。那么,问题来了,从c到控制硬件的二进制代码,中间经过了一个什么流程呢?它的过程是这样的:
一,编译预处理
读取c源文件,对其中的伪指令(以#开头)和特殊符号进行处理;
伪指令包括:宏定义: 比如:#defile PI (3.1415)
条件编译:#if (conditions) {do something} #else {do another thing} #endif
或#ifdef #ifndef #elif 等等。
头文件:#include <filename>, #include "filename" 头文件中的#ifndef __XXX_H__ #def __XXX_H__是为了防止避免头文件重复包含而导致程序出错。
特殊符号:主要有__func__、__LINE__、__FILE__等
预处理的工作其实就是将上面的伪指令和特殊符号展开,替换掉原来的c文件,这里不做语法的检查,仅仅是替换。
二,编译阶段
预处理后,进行的语法和词法的分析,确认指令都符合语法规则后,将其翻译成等价的中间代码或汇编代码。
三,优化阶段
中间代码的优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。
偏向硬件执行的优化,考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高。
四,汇编过程
将汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。OK,这里的意思就是汇编是将优化后的汇编代码,翻译成一个目标文件。但是这个这个目标文件能执行么?不能!!!因为在这个目标文件中,可能需要到其他库或者其他文件的一些函数,所以还要有一个链接的过程。这里先说说目标文件:
目标文件由段组成。通常一个目标文件中至少有两个段:
代码段 该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
数据段 主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
五,链接程序
就是刚才说的汇编后生成的目标文件需要链接到一些库或者其他文件以获取自己源码内引用的函数,符号等。链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
链接分为静态和动态。静态就是涉及到的函数和符号,直接从源文件中copy过来,形成最终的可执行文件。动态只是记录这些函数和符号的一些信息,在执行的时候才从内存中查找,并映射到可执行文件的进程虚拟空间中。
两种方式对比:静态的安全,但是可执行文件占用内存空间较大。动态的灵活,占用内存小,但是性能上可能会受到一些损害。
经过这5步,一个c文件就可以被编译成可执行的文件,默认为a.out.
参考:http://lavasoft.blog.51cto.com/62575/187229/
关于优化选项的一些介绍:
经常能看到gcc后有-O、-O1、-O2、-O3,这些代表什么意思呢?这些属于优化的选项,包括简化目标代码的长度,优化执行时间等。具体的参看下面的链接:
中文的一些简介:http://blog.chinaunix.net/uid-23916171-id-2653114.html
优化项的解释:http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Optimize-Options.html#Optimize-Options