一、编译过程
.c 未经过预处理的C源码
.h C头文件
.i 经过预处理的C源码
.s 生成的汇编语言代码
.o 编译之后产生的目标文件
解释:*.c一般使我们自己编辑的代码,使我们劳动的结晶;
*.h一般是我们手工生成的接口文件,如果愿意,也可在*.c完成后用GCC的选项-aux-info帮我们生成;
*.i是经过预处理后的源码,是由GCC在选项-E编译下自动生成的文件;
*.o是编译后产生的目标文件;
*.s是GCC在选项-S编译下生成的汇编语言代码,对于性能要求很高的程序可以先生成汇编语言文件并对汇编做优化,然后用优化后的汇编生成目标文件并链接
使用gcc编译程序时,编译过程可被细分为四个阶段:
(1)预处理
编译器读取C源程序,对其中的预处理命令(以#开头)和特殊符号进行处理。预处理命令包括主要包括三种,一是宏定义命令,二是条件编译指令,三是头文件包含指令。采用头文件的目的是使某些定义可以供多个不同的C源程序使用。在需要用到这些定义的C源程序中,只需加上#include语句即可,而不必重新定义一遍。预编译程序将头文件中的代码统统加入到源文件,进而产生输出文件。
除了以上三种预处理命令,还有特殊符号。预编译程序可以识别一些特殊符号。例如在源程序中出现的LINE表示将被解释为十进制表示的当前行号。FILE则被解释为当前编译的源程序的文件名。
预编译程序完成的工作,可以说成是对源程序的“替换”工作。经过这个过程,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。
注意:这个阶段的产物是一个源文件
(2)编译
编译程序的工作是,通过词法分析、语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的汇编代码。
在生成汇编代码过程中,可能涉及到优化处理。优化有两种:一种优化仅涉及代码本身,主要是删除公共表达式、循环优化、代码外提、无用代码赋值的删除 等。另一种优化设计具体的计算机硬件,比如,如何根据机器硬件执行指令的特点对指令进行调整优化,减少目标代码长度,提高执行效率。
注意:这个阶段产出的是对应与源文件的汇编文件。
(3)汇编
汇编代码生成以后,编译程序将中间代码转换为目标机器指令的序列,得到对应于源程序的目标文件。目标文件中存放的也就是与源程序等效的目标机器的机器语言代码。目标文件一般至少包含2个段:代码段和数据段。
注意:这个阶段产出的是对应与源文件的目标文件(机器语言文件)。
(4)链接
由第二阶段生成的若干对应于多个源程序的目标文件,并不能立即就被执行。其中存在一些问题,比如,某个源文件中的函数可能引用了另一个源文件中的某个符号(如变量或者函数等);在一个源文件中可能调用了某个库文件中的函数,等等。这些问题,需要连接程序来解决。
连接程序的主要工作就是将有关的目标文件彼此连接。也就是将在一个文件中引用的符号同该符号在另一个文件中的定义连接起来。使得所有这些目标文件成为一个能够被操作系统执行的一个整体。
注意:这个阶段会产出可执行的文件。
例如hello.c程序
hello.c:
#include
int main(void)
{
printf (Hello world!\n);
return 0;
}
1、预处理: ---(去掉注释,进行宏替换(#define相关),头文件(#include)包含等工作....)
预处理阶段,编译器将代码中的stdio.h的代码编译进来,用户使用-E选项进行查看
gccc -E hello.c -o hello.i
2、编译: ---(不同平台用的汇编语言是不一样的。编译将高级语言编译成汇编语言....)
gcc首先检查语法的规范性以及是否有语法错误等,以确定代码实际要做的工作,在检查无误后,gcc把代码编译成汇编语言。
gcc -S hello.i -o hello.s
3、汇编 ---(将汇编语言翻译成二进制的目标代码....)
把编译生成的.s文件转换成目标文件
gcc -c hello.s -o hello.o
4、链接 ---(包含各函数库的入口,得到可执行代码....)
在该阶段,在这里涉及一个重要的概念:函数库。在这个程序中并没有定义“printf"的函数实现,在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现“printf"函数的呢?
最后的答案是:系统把这些函数实现都己经被放入名为libc. so.6的库文件中去了,在没有特别指定时库函数中去,GCC会到系统默认的搜索路径“/usr/Iib”下进行查找,也就是链接到libc.so.6这样就能实现函数“printf"了,而这也就是链接的作用。
Gcc hello.o –o hello
阅读(1153) | 评论(0) | 转发(0) |