C语言的本质(33)——GCC编译器入门

GCC(GNU CompilerCollection,GNU编译器套装),是由 GNU 开发的编程语言编译器。它是以GPL许可证所发行的自由软件,也是 GNU计划的关键部分。GCC原本作为GNU操作系统的官方编译器,现已被大多数类Unix操作系统采纳为标准的编译器,GCC同样适用于微软的Windows。GCC是自由软件过程发展中的著名例子,由自由软件基金会以GPL协议发布。GCC是Linux平台下最常用的编译程序,是Linux平台编译器的事实标准。

 

 

程序的编译过程

 

对于GUN编译器来说,程序的编译要经历预处理、编译、汇编、连接四个阶段,如下图所示:

 

从功能上分,预处理、编译、汇编是三个不同的阶段,但GCC的实际操作上,它可以把这三个步骤合并为一个步骤来执行。下面我们以C语言为例来谈一下不同阶段的输入和输出情况。

 

在预处理阶段,输入的是C语言的源文件,通常为*.c。它们通常带有.h之类头文件的包含文件。这个阶段主要处理源文件中的#ifdef、 #include和#define命令。该阶段会生成一个中间文件*.i,但实际工作中通常不用专门生成这种文件,因为基本上用不到;若非要生成这种文件不可,可以利用下面的示例命令:

 

gcc -E test.c -o test.i

在编译阶段,输入的是中间文件*.i,编译后生成汇编语言文件*.s 。这个阶段对应的GCC命令如下所示:

 

GCC -S test.i -o test.s

在汇编阶段,将输入的汇编文件*.s转换成机器语言*.o。这个阶段对应的GCC命令如下所示:

 

GCC -c test.s -o test.o


最后,在连接阶段将输入的机器代码文件*.s(与其它的机器代码文件和库文件)汇集成一个可执行的二进制代码文件。这一步骤,可以利用下面的示例命令完成:

 

GCC test.o -o test

 GCC常用模式

 

这里介绍GCC追常用的两种模式:编译模式和编译连接模式。下面以一个例子来说明各种模式的使用方法。为简单起见,假设我们全部的源代码都在一个文件test.c中,要想把这个源文件直接编译成可执行程序,可以使用以下命令:

 

$ GCC -o test

这里test.c是源文件,生成的可执行代码存放在一个名为test 的文件中(该文件是机器代码并且可执行)。-o 是生成可执行文件的输出选项。如果我们只想让源文件生成目标文件(给文件虽然也是机器代码但不可执行),可以使用标记-c ,详细命令如下所示:

$ GCC -c test.c

默认情况下,生成的目标文件被命名为test.o,但我们也可以为输出文件指定名称,比如:

$ GCC -c test.c -o

上面这条命令将编译后的目标文件命名为mytest.o,而不是默认的test.o。

 

迄今为止,我们谈论的程序仅涉及到一个源文件;现实中,一个程序的源代码通常包含在多个源文件之中,这该怎么办?没关系,即使这样,用GCC处理起来也并不复杂,比如:

 $GCC -o test  first.c second.c third.c

该命令将同时编译三个源文件,即first.c、second.c和 third.c,然后将它们连接成一个可执行程序,名为test。

 

需要注意的是,要生成可执行程序时,一个程序无论有有一个源文件还是多个源文件,所有被编译和连接的源文件中必须有且仅有一个main函数,因为main函数是该程序的入口点(换句话说,当系统调用该程序时,首先将控制权授予程序的main函数)。但如果仅仅是把源文件编译成目标文件的时候,因为不会进行连接,所以main函数不是必需的。

 

gcc所遵循的部分约定规则:

.c为后缀的文件,C语言源代码文件;

.a为后缀的文件,是由目标文件构成的档案库文件;

.C,.cc或.cxx 为后缀的文件,是C++源代码文件且必须要经过预处理;

.h为后缀的文件,是程序所包含的头文件;

.i 为后缀的文件,是C源代码文件且不应该对其执行预处理;

.ii为后缀的文件,是C++源代码文件且不应该对其执行预处理;

.m为后缀的文件,是Objective-C源代码文件;

.o为后缀的文件,是编译后的目标文件;

.s为后缀的文件,是汇编语言源代码文件;

.S为后缀的文件,是经过预编译的汇编语言源代码文件。

 

常用选项

 

许多情况下,头文件和源文件会单独存放在不同的目录中。例如,假设存放源文件的子目录名为./src,而包含文件则放在层次的其他目录下,如./inc。当我们在./src 目录下进行编译工作时,如何告诉GCC到哪里找头文件呢?方法如下:

$ gcc test.c –I../inc -o test

上面的命令告诉GCC包含文件存放在./inc 目录下,在当前目录的上一级。如果在编译时需要的包含文件存放在多个目录下,可以使用多个-I 来指定各个目录:

$ gcc test.c –I../inc –I../../inc2 -o test

这里指出了另一个包含子目录inc2,较之前目录它还要在再上两级才能找到。

 

另外,我们还可以在编译命令行中定义符号常量。为此,我们可以简单的在命令行中使用-D选项即可,如下例所示:

$ gcc -DTEST_CONFIGURATION test.c -o test

上面的命令与在源文件中加入下列命令是等效的:

#define TEST_CONFIGURATION

在编译命令行中定义符号常量的好处是,不必修改源文件就能改变由符号常量控制的行为。

 

在使用Gcc编译器的时候,我们必须给出一系列必要的调用参数和文件名称。GCC编译器的调用参数大约有100多个,其中多数参数我们可能根本就用不到,这里只介绍其中最基本、最常用的参数。

GCC最基本的用法是∶gcc [options][filenames]

其中options就是编译器所需要的参数,filenames给出相关的文件名称。

-c,只编译,不链接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。

-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。

-g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。

-O,对程序进行优化编译、链接,采用这个选项,整个源代码会在编译、链接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、链接的速度就相应地要慢一些。

-O2,比-O更好的优化编译、链接,当然整个编译、链接过程会更慢。

-Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。C程序中的头文件包含两种情况∶

#include <myinc.h>
#include “myinc.h”

其中,A类使用尖括号(< >),B类使用双引号(“”)。对于A类,预处理程序cpp在系统预设包含文件目录(如/usr/include)中搜寻相应的文件,而B类,预处理程序在目标文件的文件夹内搜索相应文件。

  

警告功能

 当GCC在编译过程中检查出错误的话,它就会中止编译;但检测到警告时却能继续编译生成可执行程序,因为警告只是针对程序结构的诊断信息,它不能说明程序一定有错误,而是存在风险,或者可能存在错误。虽然GCC提供了非常丰富的警告,但前提是你已经启用了它们,否则它不会报告这些检测到的警告。

 

在众多的警告选项之中,最常用的就是-Wall选项。该选项能发现程序中一系列的常见错误警告,该选项用法举例如下:

$ gcc -Wall test.c -o test


posted on 2014-07-24 13:28  三少爷的剑123  阅读(357)  评论(0编辑  收藏  举报

导航