最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

gcc的使用

来源:动视网 责编:小OO 时间:2025-09-28 00:24:32
文档

gcc的使用

GCC使用手册作者:Clock 1.前言   GCC编译器的手册(GCCMANUAL)的英文版已经非常全面,并且结构也非常完善了,只是一直都没有中文的版本,我这次阅读了GCC编译器的主要内容,对手册的内容进行了结构性的了解,认为有必要对这次阅读的内容进行整理,为以后的工作做准备。   由于我对这个英文手册的阅读也仅仅是结构性的。因此有很多地方并没有看,所以这篇文档的内容我也只能写出部分,对于以后需要详细了解的地方,会再往这篇文档中增添内容,需要增添的内容主要是编译器的各种开关。2.GCC功能介
推荐度:
导读GCC使用手册作者:Clock 1.前言   GCC编译器的手册(GCCMANUAL)的英文版已经非常全面,并且结构也非常完善了,只是一直都没有中文的版本,我这次阅读了GCC编译器的主要内容,对手册的内容进行了结构性的了解,认为有必要对这次阅读的内容进行整理,为以后的工作做准备。   由于我对这个英文手册的阅读也仅仅是结构性的。因此有很多地方并没有看,所以这篇文档的内容我也只能写出部分,对于以后需要详细了解的地方,会再往这篇文档中增添内容,需要增添的内容主要是编译器的各种开关。2.GCC功能介
GCC使用手册 

作者:Clock 

 1.前言 

    GCC编译器的手册(GCC MANUAL)的英文版已经非常全面,并且结构也非常完善了,只是一直都没有中文的版本,我这次阅读了GCC编译器的主要内容,对手册的内容进行了结构性 的了解,认为有必要对这次阅读的内容进行整理,为以后的工作做准备。 

    由于我对这个英文手册的阅读也仅仅是结构性的。因此有很多地方并没有看,所以这篇文档的内容我也只能写出部分,对于以后需要详细了解的地方,会再往这篇文档中增添内容,需要增添的内容主要是编译器的各种开关。 

2. GCC功能介绍 

    GCC编译器完成从C、C++、objective-C等源文件向运行在特定CPU硬件上的目标代码的转换(这是任何一个编译器需要完成的任务)。 

    GCC能够处理的源文件分为C、C++、Objective-C、汇编语言等。对于这些源文件,用他们的后缀名进行标示。GCC能够处理的后缀有: 

a. *.c  *.C      (C语言) 

b. *.cxx   *.cc  (C++语言) 

c. *.m           (面向对象的C) 

d. *.i           (预处理后的C语言源文件) 

e. *.ii          (预处理后的C++语言源文件) 

f. *.s *.S       (汇编语言) 

h. *.h         (头文件) 

目标文件可以是: 

a. *.o     编译连接后的目标文件 

b. *.a     库文件 

编译器把编译生成目标代码的任务分为以下4步: 

a.预处理,把预处理命令扫描处理完毕; 

b.编译,把预处理后的结果编译成汇编或者目标模块; 

c.汇编,把编译出来的结果汇编成具体CPU上的目标代码模块; 

d.连接,把多个目标代码模块连接生成一个大的目标模块; 

3.  GCC开关 

    GCC的运行开关共分为11类,这是类开关从11个方面控制着GCC程序的运行,以达到特定的编译目的。 

3.1.  全局开关(OVERALL OPTIONS) 

    全局开关用来控制在“GCC功能介绍”中的GCC的4个步骤的运行,在缺省的情况下,这4个步骤都是要执行的,但是当给定一些全局开关后,这些步骤就会在 某一步停止执行,这产生中间结果,例如可能你只是需要中间生成的预处理的结果或者是汇编文件(比如拟的目的是为了看某个CPU上的汇编语言怎么写)。 

3.1.1.  –x  language 

    对于源文件是用什么语言编写的,可以通过文件名的后缀来标示,也可以用这开关。指定输入文件是什么语言编写的,language 可以是如下的内容 

a.  c 

b. objective-c 

c. c-header 

d. c++ 

e.cpp-output 

f.assembler 

g.assembler-with-cpp 

3.1.2.–x none 

把上一节介绍的-x开关都给关掉了。 

3.1.3.  –c 

编译成把源文件目标代码,不做连接的动作。 

3.1.4. –S 

把源文件编译成汇编代码,不做汇编和连接的动作。 

3.1.5. –E 

只把源文件进行预处理之后的结果输出来。不做编译,汇编,连接的动作。 

3.1.6.  –o file 

指明输出文件名是file。 

3.1.7. –v 

把整个编译过程的输出信息都给打印出来。 

3.1.8.–pipe 

由于gcc的工作分为好几步才完成,所以需要在过程中生成临时文件,使用-pipe就是用管道替换临时文件。 

3.2.  语言相关开关(Language Options) 

用来处理和语言相关的控制开关。 

3.2.1.–ansi 

    这个开关让GCC编译器把所有的gnu的编译器特性都给关掉,让你的程序可以和ansi标准兼容。 

    除了以上的开关外,语言相关开关还有很多,如果在以后的工作学习中遇到了再加不迟!3.3.预处理开关(Preprocessor Options) 

用来控制预处理所设置的开关。 

3.3.1. –include file 

    在编译之前,把file包含进去,相当于在所有编译的源文件最前面加入了一个#include 语句,这样做更“省油”。 

3.3.2. –imacros file 

    同-include file 一样。不过这个文件在具体编译的时候只有里面定义的宏才起作用,所以值用来在file文件里面定义宏。 

3.3.3. –nostdinc 

    在搜寻include 的文件路径中去掉标准的c语言头文件搜索路径,例如stdio.h文件就是放在标准头文件搜索路径下。 

3.3.4.  –nostdinc++ 

    同上,只是去掉的是标准C++语言的头文件搜索路径。 

3.3.5. –C 

    同-E参数配合使用。让预处理后的结果,把注释保留,让人能够比较好读它。 

3.3.6. –Dmacro 

    把macro定义为字符串’1’。 

3.3.7. –Dmacro = defn 

    把macro定义为defn。 

3.3.8.  –Umacro 

    把对macro的定义取消。 

    除了以上的开关外,预处理相关开关还有很多,如果在以后的工作学习中遇到了再加不迟! 

3.4.   汇编开关(Assembler Option) 

    用来控制汇编行为的开关。 

3.4.1.  –Wa , option 

    把option作为开关送给汇编程序。如果option里面有逗号,则作为好几行进行处理。 

3.5.连接开关(Linker Options) 

    用来控制连接过程的开关选项。 

3.5.1. object-file-name 

3.5.2. –llibrary 

    连接库文件开关。例如-lugl,则是把程序同libugl.a文件进行连接。 

3.5.3. –lobjc 

    这个开关用在面向对象的C语言文件的库文件处理中。 

3.5.4.  –nostartfiles 

    在连接的时候不把系统相关的启动代码连接进来。 

3.5.5.   –nostdlib 

    在连接的时候不把系统相关的启动文件和系统相关的库连接进来。 

3.5.6. –static 

    在一些系统上支持动态连接,这个开关则不允许动态连接。 

3.5.7. –shared 

    生成可共享的被其他程序连接的目标模块。 

    连接相关的开关还有一些,以后需要的时候再补。 

3.6.目录相

9月15日

gcc使用说明

◆ 预处理(Pre-Processing) 

◆ 编译(Compiling) 

◆ 汇编(Assembling) 

◆ 链接(Linking) 

GCC起步 

在学习使用GCC之前,下面的这个例子能够帮助用户迅速理解GCC的工作原理,并将其立即运用到实际的项目开发中去.

首先用熟悉的编辑器输入清单1所示的代码: 

清单1:hello.c 

#include 

int main(void)

{

printf ("Hello world, Linux programming!\\n" );

return 0;

}

 

然后执行下面的命令编译和运行这段程序: 

# gcc hello.c -o hello

# ./hello

Hello world, Linux programming!

 

从程序员的角度看,只需简单地执行一条GCC命令就可以了,但从编译器的角度来看,却需要完成一系列非常繁杂的工作.首先,GCC需要调用预处理程序cpp,由它负责展开在源文件中定义的宏,并向其中插入“#include”语句所包含的内容;接着,GCC会调用ccl和as将处理后的源代码编译成目标代码;最后,GCC会调用链接程序ld,把生成的目标代码链接成一个可执行程序. 

为了更好地理解GCC的工作过程,可以把上述编译过程分成几个步骤单独进行,并观察每步的运行结果.第一步是进行预编译,使用-E参数可以让GCC在预处理结束后停止编译过程: 

#  gcc -E hello.c -o hello.i

 

此时若查看hello.cpp文件中的内容,会发现stdio.h的内容确实都插到文件里去了,而其它应当被预处理的宏定义也都做了相应的处理.下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成: 

#  gcc -c hello.i -o hello.o

 

GCC默认将.i文件看成是预处理后的C语言源代码,因此上述命令将自动跳过预处理步骤而开始执行编译过程,也可以使用-x参数让GCC从指定的步骤开始编译.最后一步是将生成的目标文件链接成可执行文件: 

#  gcc hello.o -o hello

 

在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元.假设有一个由foo1.c和foo2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序foo,可以使用下面这条命令: 

#  gcc foo1.c foo2.c -o foo

如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行.如果深究起来,上面这条命令大致相当于依次执行如下三条命令: 

# gcc -c foo1.c -o foo1.o

# gcc -c foo2.c -o foo2.o

# gcc foo1.o foo2.o -o foo

 

在编译一个包含许多源文件的工程时,若只用一条GCC命令来完成编译是非常浪费时间的.假设项目中有100个源文件需要编译,并且每个源文件中都包含10000行代码,如果像上面那样仅用一条GCC命令来完成编译工作,那么GCC需要将每个源文件都重新编译一遍,然后再全部连接起来.很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的.要解决这个问题,关键是要灵活运用GCC,同时还要借助像Make这样的工具. 

警告提示功能 

GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码.先来读读清单2所示的程序,这段代码写得很糟糕,仔细检查一下不难挑出很多毛病: 

◆main函数的返回值被声明为void,但实际上应该是int; 

◆使用了GNU语法扩展,即使用long long来声明位整数,不符合ANSI/ISO C语言标准; 

◆main函数在终止前没有调用return语句. 

清单2:illcode.c 

#include 

void main(void)

{

  long long int var = 1;

  printf("It is not standard C code!\\n" );

}

 

下面来看看GCC是如何帮助程序员来发现这些错误的.当GCC在编译不符合ANSI/ISO C语言标准的源代码时,如果加上了-pedantic选项,那么使用了扩展语法的地方将产生相应的警告信息: 

# gcc -pedantic illcode.c -o illcode

illcode.c: In function `main':

illcode.c:9: ISO C does not support `long long'

illcode.c:8: return type of `main' is not `int'

 

需要注意的是,-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容,它仅仅只能用来帮助Linux程序员离这个目标越来越近.或者换句话说,-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,但不是全部,事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况,才有可能被GCC发现并提出警告. 

除了-pedantic之外,GCC还有一些其它编译选项也能够产生有用的警告信息.这些选项大多以-W开头,其中最有价值的当数-Wall了,使用它能够使GCC产生尽可能多的警告信息: 

# gcc -Wall illcode.c -o illcode

illcode.c:8: warning: return type of `main' is not `int'

illcode.c: In function `main':

illcode.c:9: warning: unused variable `var'

GCC给出的警告信息虽然从严格意义上说不能算作是错误,但却很可能成为错误的栖身之所.一个优秀的Linux程序员应该尽量避免产生警告信息,使自己的代码始终保持简洁、优美和健壮的特性.在处理警告方面,另一个常用的编译选项是-Werror,它要求GCC将所有的警告当成错误进行处理,这在使用自动编译工具(如Make等)时非常有用.如果编译时带上-Werror选项,那么GCC会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改.只有当相应的警告信息消除时,才可能将编译过程继续朝前推进.执行情况如下: 

# gcc -Wall -Werror illcode.c -o illcode

cc1: warnings being treated as errors

illcode.c:8: warning: return type of `main' is not `int'

illcode.c: In function `main':

illcode.c:9: warning: unused variable `var'

 

对Linux程序员来讲,GCC给出的警告信息是很有价值的,它们不仅可以帮助程序员写出更加健壮的程序,而且还是跟踪和调试程序的有力工具.建议在用GCC编译源代码时始终带上-Wall选项,并把它逐渐培养成为一种习惯,这对找出常见的隐式编程错误很有帮助. 

库依赖 

在Linux下开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能.从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合.虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的情况都是这样.正因如此,GCC在编译时必须有自己的办法来查找所需要的头文件和库文件. 

GCC采用搜索目录的办法来查找所需要的文件,-I选项可以向GCC的头文件搜索路径中添加新的目录.例如,如果在/home/xiaowp/include/目录下有编译时所需要的头文件,为了让GCC能够顺利地找到它们,就可以使用-I选项: 

# gcc foo.c -I /home/xiaowp/include -o foo

 

同样,如果使用了不在标准位置的库文件,那么可以通过-L选项向GCC的库文件搜索路径中添加新的目录.例如,如果在/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so,为了让GCC能够顺利地找到它,可以使用下面的命令: 

# gcc foo.c -L /home/xiaowp/lib -lfoo -o foo

 

值得好好解释一下的是-l选项,它指示GCC去连接库文件libfoo.so.Linux下的库文件在命名时有一个约定,那就是应该以lib三个字母开头,由于所有的库文件都遵循了同样的规范,因此在用-l选项指定链接的库文件名时可以省去lib三个字母,也就是说GCC在对-lfoo进行处理时,会自动去链接名为libfoo.so的文件. 

Linux下的库文件分为两大类分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),两者的差别仅在程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的.默认情况下,GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库.例如,如果在/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令: 

# gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo

代码优化 

代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能.GCC提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数.对于不同版本的GCC来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3. 

编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1.在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化.选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等.选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作.通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快.许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点. 

下面通过具体实例来感受一下GCC的代码优化功能,所用程序如清单3所示. 

清单3: optimize.c 

#include  

int main(void)

{

  double counter;

  double result;

  double temp;

  for (counter = 0; 

   counter < 2000.0 * 2000.0 * 2000.0  / 20.0 + 2020; 

   counter += (5 - 1) / 4) {

    temp = counter / 1979;

    result  = counter;    

  }

  printf("Result is %lf\\n", result);

  return 0;

}

 

首先不加任何优化选项进行编译: 

# gcc -Wall optimize.c -o optimize

 

借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间: 

# time ./optimize

Result is 400002019.000000

real    0m14.942s

user    0m14.940s

sys     0m0.000s

 

接下去使用优化选项来对代码进行优化处理: 

# gcc -Wall -O optimize.c -o optimize

 

在同样的条件下再次测试一下运行时间: 

# time ./optimize

Result is 400002019.000000

real    0m3.256s

user    0m3.240s

sys     0m0.000s

 

对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的14秒缩短到了3秒.这个例子是专门针对GCC的优化功能而设计的,因此优化前后程序的执行速度发生了很大的改变.尽管GCC的代码优化功能非常强大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的代码.如果编写的代码简短,并且逻辑性强,编译器就不会做更多的工作,甚至根本用不着优化.

优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码: 

◆ 程序开发的时候 优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化. 

◆ 资源受限的时候 一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果. 

◆ 跟踪调试的时候 在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难. 

调试 

一个功能强大的调试器不仅为程序员提供了跟踪程序执行的手段,而且还可以帮助程序员找到解决问题的方法.对于Linux程序员来讲,GDB(GNU Debugger)通过与GCC的配合使用,为基于Linux的软件开发提供了一个完善的调试环境.默认情况下,GCC在编译时不会将调试符号插入到生成的二进制代码中,因为这样会增加可执行文件的大小.如果需要在编译时生成调试符号信息,可以使用GCC的-g或者-ggdb选项.GCC在产生调试符号时,同样采用了分级的思路,开发人员可以通过在-g选项后附加数字1、2或3来指定在代码中加入调试信息的多少.默认的级别是2(-g2),此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息.级别3(-g3)包含级别2中的所有调试信息,以及源代码中定义的宏.级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储之用.回溯跟踪指的是监视程序在运行过程中的函数调用历史,堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法,两者都是经常用到的调试手段. 

GCC产生的调试符号具有普遍的适应性,可以被许多调试器加以利用,但如果使用的是GDB,那么还可以通过-ggdb选项在生成的二进制代码中包含GDB专用的调试信息.这种做法的优点是可以方便GDB的调试工作,但缺点是可能导致其它调试器(如DBX)无法进行正常的调试.选项-ggdb能够接受的调试级别和-g是完全一样的,它们对输出的调试符号有着相同的影响. 

需要注意的是,使用任何一个调试选项都会使最终生成的二进制文件的大小急剧增加,同时增加程序在执行时的开销,因此调试选项通常仅在软件的开发和调试阶段使用.调试选项对生成代码大小的影响从下面的对比过程中可以看出来: 

# gcc optimize.c -o optimize

# ls optimize -l

-rwxrwxr-x  1 xiaowp   xiaowp  119 Nov 20 08:53 optimize  (未加调试选项)

# gcc -g optimize.c -o optimize

# ls optimize -l

-rwxrwxr-x  1 xiaowp   xiaowp  158 Nov 20 08:54 optimize  (加入调试选项)

虽然调试选项会增加文件的大小,但事实上Linux中的许多软件在测试版本甚至最终发行版本中仍然使用了调试选项来进行编译,这样做的目的是鼓励用户在发现问题时自己动手解决,是Linux的一个显著特色.为调试编译代码(Compiling Code for Debugging)为了使 gdb 正常工作, 你必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号. gdb 利用这些信息使源代码和机器码相关联. 

◆在编译时用 -g 选项打开调试选项. 

  

gdb 基本命令

     gdb 支持很多的命令使你能实现不同的功能. 这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令, 表27.1列出了你在用 gdb 调试时会用到的一些命令. 想了解 gdb 的详细使用请参考 gdb 的指南页. 

  表 27.1. 基本 gdb 命令.

命   令 描  述 

file 装入想要调试的可执行文件. 

kill 终止正在调试的程序. 

list 列出产生执行文件的源代码的一部分. 

next 执行一行源代码但不进入函数内部. 

step 执行一行源代码而且进入函数内部. 

run 执行当前被调试的程序 

quit 终止 gdb 

watch 使你能监视一个变量的值而不管它何时被改变. 

break 在代码里设置断点, 这将使程序执行到这里时被挂起. 

make 使你能不退出 gdb 就可以重新产生可执行文件. 

shell 使你能不离开 gdb 就执行 UNIX shell 命令.  

gdb 支持很多与 UNIX shell 程序一样的命令编辑特征. 你能象在 bash 或 tcsh里那样按 Tab 键让gdb 帮你补齐一个唯一的命令, 如果不唯一的话 gdb 会列出所有匹配的命令. 你也能用光标键上下翻动历史命令. 

下面还是通过一个具体的实例说明如何利用调试符号来分析错误,所用程序见清单4所示. 

清单4:crash.c 

#include  

int main(void)

{

  int input =0;

  printf("Input an integer:" );

  scanf("%d", input);

  printf("The integer you input is %d\\n", input);

  return 0;

}

 

编译并运行上述代码,会产生一个严重的段错误(Segmentation fault)如下: 

# gcc -g crash.c -o crash

# ./crash

Input an integer:10

Segmentation fault

 

为了更快速地发现错误所在,可以使用GDB进行跟踪调试,方法如下: 

# gdb crash

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)

……

(gdb)

 

当GDB提示符出现的时候,表明GDB已经做好准备进行调试了,现在可以通过run命令让程序开始在GDB的监控下运行: 

(gdb) run

Starting program: /home/xiaowp/thesis/gcc/code/crash

Input an integer:10

Program received signal SIGSEGV, Segmentation fault.

0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6

 

仔细分析一下GDB给出的输出结果不难看出,程序是由于段错误而导致异常中止的,说明内存操作出了问题,具体发生问题的地方是在调用_IO_vfscanf_internal ( )的时候.为了得到更加有价值的信息,可以使用GDB提供的回溯跟踪命令backtrace,执行结果如下: 

(gdb) backtrace

#0  0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6

#1  0xbffff0c0 in  ()

#2  0x4008e0ba in scanf () from /lib/libc.so.6

#3  0x08048393 in main () at crash.c:11

#4  0x40042917 in __libc_start_main () from /lib/libc.so.6

 

跳过输出结果中的前面三行,从输出结果的第四行中不难看出,GDB已经将错误定位到crash.c中的第11行了.现在仔细检查一下: 

(gdb) frame 3

#3  0x08048393 in main () at crash.c:11

11       scanf("%d", input);

 

使用GDB提供的frame命令可以定位到发生错误的代码段,该命令后面跟着的数值可以在backtrace命令输出结果中的行首找到.现在已经发现错误所在了,应该将 

scanf("%d", input);

改为

scanf("%d", &input);

完成后就可以退出GDB了,命令如下: 

(gdb) quit

 

GDB的功能远远不止如此,它还可以单步跟踪程序、检查内存变量和设置断点等. 

调试时可能会需要用到编译器产生的中间结果,这时可以使用-save-temps选项,让GCC将预处理代码、汇编代码和目标代码都作为文件保存起来.如果想检查生成的代码是否能够通过手工调整的办法来提高执行性能,在编译过程中生成的中间文件将会很有帮助,具体情况如下: 

# gcc -save-temps foo.c -o foo

# ls foo*

foo  foo.c  foo.i  foo.s

 

GCC支持的其它调试选项还包括-p和-pg,它们会将剖析(Profiling)信息加入到最终生成的二进制代码中.剖析信息对于找出程序的性能瓶颈很有帮助,是协助Linux程序员开发出高性能程序的有力工具.在编译时加入-p选项会在生成的代码中加入通用剖析工具(Prof)能够识别的统计信息,而-pg选项则生成只有GNU剖析工具(Gprof)才能识别的统计信息. 

最后提醒一点,虽然GCC允许在优化的同时加入调试符号信息,但优化后的代码对于调试本身而言将是一个很大的挑战.代码在经过优化之后,在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句有可能因为循环展开而变得到处都有,所有这些对调试来讲都将是一场噩梦.建议在调试的时候最好不使用任何优化选项,只有当程序在最终发行的时候才考虑对其进行优化.

上次的培训园地中介绍了GCC的编译过程、警告提示功能、库依赖、代码优化和程序调试六个方面的内容.这期是最后的一部分内容. 

加速 

在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接.这些过程实际上是由不同的程序负责完成的.大多数情况下GCC可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理. 

这样做有一个很明显的缺点,就是GCC在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢.例如,GCC在处理一个源文件时,可能需要一个临时文件来保存预处理的输出、一个临时文件来保存编译器的输出、一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间.当软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很沉重. 

解决的办法是,使用Linux提供的一种更加高效的通信方式—管道.它可以用来同时连接两个程序,其中一个程序的输出将被直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存. 

在编译过程中使用管道是由GCC的-pipe选项决定的.下面的这条命令就是借助GCC的管道功能来提高编译速度的: 

# gcc -pipe foo.c -o foo

 

在编译小型工程时使用管道,编译时间上的差异可能还不是很明显,但在源代码非常多的大型工程中,差异将变得非常明显. 

文件扩展名 

在使用GCC的过程中,用户对一些常用的扩展名一定要熟悉,并知道其含义.为了方便大家学习使用GCC,在此将这些扩展名罗列如下: 

.c C原始程序; 

.C C++原始程序; 

.cc C++原始程序; 

.cxx C++原始程序; 

.m Objective-C原始程序; 

.i 已经过预处理的C原始程序; 

.ii 已经过预处理之C++原始程序; 

.s 组合语言原始程序; 

.S 组合语言原始程序; 

.h 预处理文件(标头文件); 

.o 目标文件; 

.a 存档文件. 

GCC常用选项 

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

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

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

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

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

-Idirectory 在包含文件搜索路径的起点处添加指定目录; 

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

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

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

-v 启动所有警报; 

-Wall 在发生警报时取消编译操作,即将警报看作是错误; 

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

-w 禁止所有的报警. 

 

文档

gcc的使用

GCC使用手册作者:Clock 1.前言   GCC编译器的手册(GCCMANUAL)的英文版已经非常全面,并且结构也非常完善了,只是一直都没有中文的版本,我这次阅读了GCC编译器的主要内容,对手册的内容进行了结构性的了解,认为有必要对这次阅读的内容进行整理,为以后的工作做准备。   由于我对这个英文手册的阅读也仅仅是结构性的。因此有很多地方并没有看,所以这篇文档的内容我也只能写出部分,对于以后需要详细了解的地方,会再往这篇文档中增添内容,需要增添的内容主要是编译器的各种开关。2.GCC功能介
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top