Linux下C语言编译过程

  • Post author:
  • Post category:linux




一、编译过程

在Linux下,通常我们用gcc来生成可执行文件,通常为gcc *.c,默认生成可执行文件a.out。其实编译(包括连接)的命令:gcc *.c可分解为如下四个步骤。

  1. 预编译(Preprocessing);
  2. 编译(Compilation);
  3. 汇编(Assembly);
  4. 链接(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 静态链接



版权声明:本文为weixin_46517066原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。