一、什么是GCC
GCC是以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC的初衷是为GNU操作系统专门编写一款编译器,现已被大多数类Unix操作系统(如Linux、BSD、MacOS X等)采纳为标准的编译器,甚至在微软的Windows上也可以使用GCC。GCC支持多种计算机体系结构芯片,如x86、ARM、MIPS等,并已被移植到其他多种硬件平台。这里我们先主要讲解Linux下C语言gcc的使用。
1.gcc的使用方法
gcc [选项] 文件名
在linux下我们可以使用 gcc --help 查看gcc的各个选项
2.gcc的常见选项
gcc的编译过程
gcc编译的过程可分解为4个大的步骤:
预处理(Preprocessing)
编译(Compilation)
汇编(Assembly)
链接(Linking)
下面我们讲具体的讲一下gcc编译步骤
1、预处理
预处理是读取c源程序,对其中的伪指令和特殊符号进行“替代”处理;经过此处理,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,仍然是C文件,但内容有所不同。伪指令主要包括以下三个方面:
(1)宏定义指令,如#define NAME TokenString, #undef以及编译器内建的一些宏,如__DATE__,FILE, LINE, TIME,__FUNCTION__等。
(2)条件编译指令,如#ifdef, #ifndef, #else, #elif, #endif等。
(3)头文件包含指令,如#include “FileName”或者#include 等。
预处理的过程主要包括以下过程:
- 将所有的#define删除,并且展开所有的宏定义
- 处理所有的条件预编译指令,比如#if 、#ifdef、#elif、#else、#endif等
- 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。
- 删除所有注释“//”和“ /* */”
- 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
- 保留所有的#pragma编译器指令,因为编译器需要使用它们
通常使用以下命令来进行预处理,参数-E表示只进行预处理:
gcc -E hello.c -o hello.i
也可以使用以下指令完成预处理过程,其中从cpp是预处理器:
cpp hello.c > hello.i
预处理后的结果hello.i还是c语言源代码,我们可以使用cat或vim命令查看它的代码
vim hello.i
2、编译
编译程序所要做的工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。关于编译环节想要了解更多的可以参考一下其他博客介绍。
我们可以使用下面命令进行编译生成汇编文件:
gcc -S hello.i > hello.s
我们可以使用cat命令查看它的代码:
cat hello.s
3、汇编
汇编过程实际上把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个c语言源程序,都将最终经过一处理而得到相应的目标文件。
我们可以用下面命令进行汇编:
gcc -c hello.s -o hello.o
4、链接
汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,在某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件的函数,等等,所有的这些问题,都需要经过链接才能得以解决,链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体,也就是可执行程序,根据开发人员指定的库函数的链接方式的不同,链接处理可分为两种:①静态链接 ②动态链接。
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越,在某些情况下动态链接可能带来一些性能上的损害。