一、编译过程
在Linux下,通常我们用gcc来生成可执行文件,通常为gcc *.c,默认生成可执行文件a.out。其实编译(包括连接)的命令:gcc *.c可分解为如下四个步骤。
- 预编译(Preprocessing);
- 编译(Compilation);
- 汇编(Assembly);
-
链接(Linking);
1、预处理(Preprocessing)
我们先写一个简单的代码,先看看源代码与预处理后的区别:
leihaojie@Ubuntu-14:~/fi$ vim hello.c
#include <stdio.h>
int test(void);
int main(int argc, char **argv)
{
printf("this program compiled on %s:%s\n", __DATE__, __TIME__);
printf("hello word\n");
test();
return 0;
}
int test(void)
{
printf("entry into %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__);
}
预处理过后
leihaojie@Ubuntu-14:~/fi$ gcc -E hello.c -o hello.i
预处理是读取c源程序,对其中的伪指令(以#开头的指令,也就是宏)和特殊符号进行“替代”处理;经过此处理,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。
预处理包括以下几个过程:
-
将所有的#define删除,并且展开所有的宏定义;
在hello.c,我用了几个宏定义:
__DATE__; //日期;
__FILE__; //在哪个文件下;
__LINE__; //在哪一行;
__TIME__; //具体时间
__FUNCTION__; //引用哪个语句,编译阶段再处理;
- 处理所有的条件预编译指令,例如:#if #ifdef #elif #else #endif等;
- 删除所有注释 “//”和”/* */”;
- 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号;
- 处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置;
- 保留所有的#pragma编译器指令,因为编译器需要使用它们;
2、编译(Compilation)
编译阶段就是检查语法是否错误,然后生成汇编代码。
下面的命令生成汇编文件:
leihaojie@Ubuntu-14:~/fi$ gcc -S hello.i -o hello.s
用cat查看 hello.s文件,得到如下汇编代码
在这里,我们使用PC的编译器gcc就会编译生成x86的汇编,而使用ARM的编译器则生成ARM的汇编文件。同一份C代码不作任何修改,使用不同的编译器编译就生成在不同机器上运行的程序,这就是C程序的可移植性。我们在PC上编写程序,在PC上用ARM的交叉编译器,生成在ARM平台上的可执行程序的过程叫做交叉编译。
3、汇编(Assembly)
汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。这个过程不需要考虑,因为这个过程由编译器翻译成自己的语言,从而得到目标文件。目标文件由段组成,通常目标文件有两个段,文本段和数据段。
以下是生成目标文件的代码:
leihaojie@Ubuntu-14:~/fi$ gcc -c hello.s -o hello.o
4、链接(Linking
)
汇编程序生成的目标文件并不能立即就被执行,例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。这些问题,都需要经链接程序的处理方能得以解决。根据开发人员指定的库函数的链接方式的不同,链接处理可分为两种:静态链接和动态链接。
使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。
程序进行静态链接并查看程序大小:
程序进行动态链接并查看程序大小:
二、gcc通用选项
GNU编译器套件(GNU Compiler Collection, GCC)包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。
通用选项:
-E | 只进行预处理,不编译 |
---|---|
-S | 只编译,不汇编 |
-c | 只编译、汇编,不链接 |
-g | 包含调试信 |
-o | 输出成指定文件名 |
-I | 指定include包含文件的搜索目录 |
-L | 指定链接所需库(动态库或静态库)所在路径 |
-ansi | ANSI标准 |
-std=c99 | C99标准 |
-Werror | 不区分警告和错误,遇到任何警告都停止编译 |
-Wall | 开启大部分警告提示 |
-static | 静态链接 |