Chinaunix首页 | 论坛 | 博客
  • 博客访问: 770695
  • 博文数量: 95
  • 博客积分: 6011
  • 博客等级: 准将
  • 技术积分: 1342
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-12 16:58
文章分类

全部博文(95)

文章存档

2009年(44)

2008年(51)

我的朋友

分类: LINUX

2008-04-12 22:50:37

以前对编译系统没有多大了解,仅仅当成一个工具来使用,从没有对其进行从内而外的理解。前几天看了关于GCC的东西,现在想来,好好总结一下,怕过了两天又忘了,算是留下研究的证明吧, 呵呵。

1.       为什么使用GCC。关于这个问题,从来没有问过自己,是啊,有很多的编译器,为什么使用GCC呢,看一下GCC的全称就知道答案了:GNU Compiler Collection. 这就意味着其遵循GNU,人人都可以用,人人都可以优化,也意味它是安全的,全世界几亿双眼睛盯着呢,敢造次吗!它会越来越进化,因为全世界几亿人都时刻准备着为了它的优化做奋斗呢,就跟实现共产主义似的。

2.       编译器的处理过程:

编译过程一般可以分为四个部分,分别由四个功能模块来完成。

 

从功能来看:

 

源程序-> 预处理cpp ->  编译ccl  ->  汇编as  ->  链接ld ->可执行文件 

 

 

从文件流来看:

 


 



 

                  预处理cpp             编译ccl      汇编as                 链接ld    

c―――――>。i―――――>。S―――――>。o―――――>可执行文件

 

现罗列一下编译过程中的文件的类型:

原始程序码的扩展名指出所用编写程序所用的语言,以及相对应的处理方法:

       .c    C 原始程序                            预处理、编译、汇编

       .C    C++ 原始程序                        预处理、编译、汇编

       .cc   C++ 原始程序                        预处理、编译、汇编

       .cxx  C++ 原始程序                        预处理、编译、汇编

      .m    Objective-C 原始程序               预处理、编译、汇编

       .i    已经过预处理之 C 原始程序       编译、汇编

       .ii   已经过预处理之 C++ 原始程序     编译、汇编

       .s    组合语言原始程序                  汇编

       .S    组合语言原始程序                  预处理、汇编

       .h    预处理文件(标头文件)              (不常出现在指令行)

       其他扩展名的文件是由连结程序来处理,通常有:

       .o     Object file

       .a     Archive file

.so  Shared object file

 

 Linux程序员可以根据自己的需要让GCC在编译的任何阶段结束,以便检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制(可对任一中间类型的代码进行处理即可从任一口入,也可以生成任一中间段的代码即可从任一口出,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。和其它常用的编译器一样,GCC也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更 高的代码。GCC提供了30多条警告信息和三个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,GCC还对标准的CC++语言进行了大量的扩展,提高程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。从程序员的角度看,只需简单地执行一条GCC命令就可以了,但从编译器的角度来看,却需要完成一系列非常繁杂的工作。首先,GCC需要调用预处理程序 cpp,由它负责展开在源文件中定义的宏,并向其中插入“#include”语句所包含的内容;接着,GCC会调用cclas将处理后的源代码编译成目标代码;最后,GCC会调用链接程序ld,把生成的目标代码链接成一个可执行程序。

 

3.       GCC编译器的功能

 

(1)       工作流程的介绍

 预编译:源代码中的预编译指示以"#"为前缀。对#include“”与#include<>作一下解释。

#include“”:要求预编译模块先到“”指定的位置去找->再到-Idirname中去找->到系统默认的目录(一般为/usr/include)中找。

#include<>:到系统默认的目录(一般为/usr/include)中找。

源代码经过预编译模块CPP完成如下

  1. "include"中包含的文件拷贝到要编译的源文件中。
  2. 用实际值替代"define"的文本。
  3. 在调用宏的地方进行宏替换。

这就使我们能够在整个源文件中使用符号常量,而符号常量是在一个地方定义的,如果它的值发生了变化,所有使用符号常量的地方 都能自动更新。如果想就从源程序->生成预处理的代码,命令如下:

gcc –E  sample.c  –o  sample.i

如果要指定对include中包含文件的收搜位置则如下(如果放在系统默认的位置(/usr/include)则不用特别指出):

gcc –E  sample.c  -I dirname  –o  sample.i

 

但是在实践中,几乎不需要单独使用 -E 选项,而是让它把输出传送给编译器。

 

编译:其实就是作语法检查,作为一个中间步骤,gcc把代码翻译成汇编语言(。S)。它一定要这样做,它必须通过分析你的代码而搞清楚你究竟想要做什么。如果你犯了语法错误,它就会告诉你,这样编译就失败了。

单独的过程命令如下:

 

gcc –S  sample.c / sample.i  -o  sample.S

 

汇编as 汇编语言代码转换为目标代码。事实上目标代码并不能在CPU上运行, 但它离完成已经很近了。编译器选项 -c .c 文件转换为以 .o 为扩展名的目标文件。

单独的过程命令如下:

 

gcc –c sample.c / sample.i / sample.S   -o sample.o

 

如果不用-o选项,系统自动生成sample.o文件。汇编过程生成目标代码。

 

链接:为什么要链接。a. 我们知道一个程序在执行时都是从main函数开始执行的,一个程序可以由许多源程序构成且只有一个源程序中含有main函数,经过前边这三边,生成相应的目标文件,这些普通的目标程序只有跟含有main函数的目标程序经过链接才能生成一个可执行文件b. 由于现在库的使用(头文件(变量和函数的声明即接口,所以在安装库时要注意库的安装位置,以便于应用程序对头文件的引用)+函数实现(函数的定义的目标代码)),在预编译阶段已经处理了头文件相关的操作,头文件中有函数的声明,但是没有相关函数的实现目标代码,如果不连接,在执行时就不能找到函数的具体实现信息,无法实现函数的功能。所在必须在执行前,实行函数的声明与实现的一一对应。头文件的处理是在第一阶段预处理中完成的,而库的链接是在第四个阶段中完成的。

单独的命令形式:

gcc  sample.c / sample.i / sample.S / sample.o  -o  sample

如果要指定链接过程中的库的位置则如下(如果库是安装在系统默认的位置(/usr/lib)则不用特别指出,系统在链接时会自动去那个地方找库文件的目标代码部分。另外库文件在生成时按系统默认规定都以lib开头,所以在写–lname时,实是省略了前边的lib,相当于–llibname)

gcc  sample.c / sample.i / sample.S / sample.o  -L dirname  –lname  -o  sample

 

(2)       警告提示与代码优化功能:

 首先区别报错与警告的区别,系统在报错时是停止编译的,而在警告时是不停止编译的。虽然警告能继续编译过程,但是正确对待警告信息,能帮助我们提高代码的质量。一般以W开头的选项都与警告有关。即在命令上加上-Wall选项,就打开了所有的警告功能。另一个常用编译器选项是优化选项 -O# ( -O2估,是字母Ooptimize)。这是告诉编译器你需要什么级别的优化。编译器具有一整套技巧可以使你的代码运行得更快一点。 对于一般的小程序,你可能注意不到差别,但对于大型程序来说,它可以大幅度提高运行速度。你会经常碰到它,所以你应该知道它的意思。一般用-o2

 

(3)       调试功能:这一部分没有进行怎么实现,偶就不敢乱造次了,留给以后吧。但是介绍2个调试器。

GDBGNU Debugger.

KDbgKDE’s Debugger

4.       常用的命令选项

 GCC作为LinuxC/C++重要的编译环境,功能强大,编译选项繁多。为了方便大家日后编译方便,在此将常用的选项及说明列数如下:

 -c :      通知GCC取消链接步骤,即编译源码并在最后生成目标文件;

 -Dmacro    定义指定的宏,使它能够通过源码中的#ifdef进行检验;

-E :           不经过编译预处理程序的输出而输送至标准输出;

 -g3:          获得有关调试程序的详细信息,它不能与-o选项联合使用;

 -I directory: directory所指出的目录加入到程序头文件目录列表中,是预编译过程中使用的参数; 在程序设计中,如果我们需要的这种包含文件分别分布在不同的目录中,就需要逐个使用-I选项给出搜索路径。

 -L directory: directory所指出的目录加入到程序函数档案库文件的目录列表中,是在连接过程中使用的参数。在预设状态下,连接程序ld在系统的预设路径中(/usr/lib)寻找所需要的库文件, 但是这个选项告诉连接程序,首先到-L指定的目录中去寻找,然后到系统预设路径中寻找,如果函数库存放在多个目录下,就需要依次使用这个选项,给出相应的存放目录。

 -llibrary:    提示链接程序在创建最终可执行文件时包含指定的库;

 -shared:   将文件编译成动态库。

 -O-O2-O3: 将优化状态打开,该选项不能与-g选项联合使用;

 -S:           要求编译程序生成来自源代码的汇编程序输出;

 -Wall:        启动所有警报;

-Werror:       在发生警报时取消编译操作,即把报警当作是错误;

 -w :          禁止所有的报警。

(-I dir和-L dir,就是通知系统,其所指的目录优于系统默认的目录)

阅读(1726) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~