一、编译过程
   
在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 | 静态链接 | 
 
