Linux下C语言编译过程

一、编译过程

在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指定链接所需库(动态库或静态库)所在路径
-ansiANSI标准
-std=c99C99标准
-Werror不区分警告和错误,遇到任何警告都停止编译
-Wall开启大部分警告提示
-static静态链接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值