《Linux 高级程序设计(第三版)》——2.2 GCC/GDB编译调试工具基础-阿里云开发者社区

开发者社区> 异步社区> 正文

《Linux 高级程序设计(第三版)》——2.2 GCC/GDB编译调试工具基础

简介:
+关注继续查看

本节书摘来自异步社区《Linux 高级程序设计(第三版)》一书中的第2章,第2.2节,作者:杨宗德 , 吕光宏 , 刘雍著,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.2 GCC/GDB编译调试工具基础

Linux 高级程序设计(第三版)
GCC/G++是GNU最优秀的自由软件之一,它主要提供C/C++程序的编译工作。Linux下的C、C++程序开发过程中,一般都采用GCC/G++/GDB工具。将C语言程序编译成一个可执行文件一般都需经过以下4个步骤。

(1)预处理(Preprocessing):对源代码文件中的文件包含、宏定义、预编译语句进行分析和替换。

(2)编译(Compilation):根据编译器的语法规则,将高级语言转换为以.s为后缀的汇编语言文件。

(3)汇编(Assembly):将.S和.s为后缀的汇编语言文件经过预编译和汇编成为以.o为后缀的目标文件。

(4)连接(Linking):当所有的目标文件都生成之后,将它们安排到可执行程序中恰当的位置上,同时,该程序所调用到的库函数也需要连接到合适的地方。

2.2.1 GCC/G++简单介绍
1.GCC版本信息
GCC/G++是GNU最优秀的自由软件之一,除了可以作为C/C++语言的编译器外,还可以编译其他语言程序,在Shell提示符号下键入“gcc -v”,屏幕上就会显示出目前正在使用的GCC的版本及相关信息:

[root@localhost ~]# gcc -v   //查找本机系统gcc信息
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/ share/info --enable-shared 
--enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit   
--disable-libunwind-exceptions --enable-libgcj-multifile   
--enable- languages= c,c++, objc, java, f95,ada 
--enable-java-awt=gtk --with-java-home=/usr/lib/jvm/java-1.4.2-gcj- 1.4.2.0/jre –host =i386 –redhat -linux
Thread model: posix
gcc version 4.4.3 20110519 (Red Hat 5.0.0-8)

“Target: i386-redhat-linux”信息说明当前正在使用的GCC是i386、i486、i586微处理器写的。这3种微处理器的芯片所编译而成的程序代码,彼此间可兼容使用。如果是交叉编译工具,则为其他类型的处理器,例如ARM。

“Configured with:”用来标识当前GCC相关配置,具体包括如下。

  • --prefix=/usr:安装路径。
  • --mandir=/usr/share/man:man手册路径。
  • --infodir=/usr/share/info:info信息路径。
  • --enable-shared:生成共享库。
  • --enable-threads=posix:线程类型为posix。
  • --enable-checking=release:检查内部发行版本一致性。
  • --with-system-zlib:安装zlib库。
  • --enable-cxa_atexit:使用cxa_atexit,而不是atexit。
  • --disable-libunwind-exceptions:禁止libunwind异常。
  • --enable-libgcj-multifile:将所有.java编译到.class文件。
  • --enable-languages=c,c++,objc,java,f95,ada:支持的语言类型。
  • --enable-java-awt=gtk:Java类型。
  • --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre:Java所在主目录。
  • --host=i386-redhat-linux:主机类型。

“Thread model: posix”信息说明当前使用的线程为posix线程库。

“gcc version 4.4.3 20110519 (Red Hat 4.0.0-8)”信息说明当前GCC为4.4版本。

安装后的GCC主要目录结构如下:

[root@localhost sourse]# rpm -ql gcc //查看gcc文件信息
//rpm命令在Ubuntu下不支持,此处为Redhat平台
……
/usr/bin/cc       //cc命令所在路径
/usr/bin/gcc       //gcc命令所在路径
/usr/bin/i386-redhat-linux-gcc   //i386平台编译命令
……
/usr/lib/gcc       //库所在路径
/usr/lib/gcc/i386-redhat-linux
/usr/lib/gcc/i386-redhat-linux/4.0.0
……
/usr/libexec/gcc
/usr/libexec/gcc/i386-redhat-linux
/usr/libexec/gcc/i386-redhat-linux/4.0.0
……
/usr/share/doc/gcc-4.0.0    //共享文件路径
…… 
/usr/share/info/gcc.info.gz   //info信息路径
…… 
/usr/share/locale/be/LC_MESSAGES/gcc.mo
……
/usr/share/man/man1/gcc.1.gz
……

2.GCC编译过程
GCC/G++是GNU中C和C++的编译器,其编译格式如下:

gcc [option|filename ]...
g++ [option|filename ]...

其中options就是编译器所需要的参数,filename是文件名称。Linux下的C和C++编译器将程序编译成一个可执行文件需要经过以下4个步骤。

(1)预处理(也称预编译,Preprocessing):即进行预处理。在预处理过程中,对源代码文件中的文件包含、预编译语句进行分析,使用-E参数。

(2)编译(Compilation):即调用cc进行编译。这个阶段根据输入文件生成以.s为后缀的汇编文件,使用-s参数。

(3)汇编(Assembly):即调用as进行编译,将.S和.s为后缀的汇编语言文件汇编成为以.o为后缀的目标文件,使用-c参数。

(4)连接(Linking):当所有的目标文件都生成之后,调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排到可执行程序中恰当的位置上,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方,使用-o参数。

除非使用了-c、-S或-E选项(这些参数可以在附录中查找到)或者编译错误阻止了过程的进行,否则连接总是最后的步骤。在连接阶段,所有对应于源程序的.o文件,-l库文件 将按命令行中的顺序传递给连接器。

对源代码文件来说,后缀名控制着缺省设定。

GCC:认为预处理后的文件(.i)是C文件,并且设定C形式的连接。
G++:认为预处理后的文件(.ii)是C++文件,并且设定C++形式的连接。
在编辑源文件时,源文件后缀名标识源文件的语言类型以及后期的操作,各语言类型说明如表2-2所示。
screenshot

screenshot

2.2.2 GDB调试工具简介
GNU的调试器称为GDB,该调试工具是一个交互式工具,在字符模式下工作。很多程序员习惯于图形界面的程序开发,如VC、VB等集成开发环境,但是在UNIX/Linux环境下开发软件,GDB比传统C语言的开发环境具有更强大的功能。GDB作为功能强大的调试工具,可完成如下的调试任务。

(1)设置断点。

(2)监视程序变量的值。

(3)程序的单步执行。

(4)修改变量的值。

默认情况下,Linux系统安装了GDB调试工具。查看本机GDB版本信息的命令如下:

[root@localhost ch0202]# gdb -v       //查看gdb版本信息
GNU gdb Red Hat Linux (6.3.0.0-1.21rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".  //i386调试器

为了使用GDB调试工具,在编译源文件时必须使用-g选项(即gcc -c -g *.c)加上调试信息。另外,如果使用makefile文件,还可以在makefile(关于makefile本书在后面章节将详细介绍)中定义CFLAGS变量。

CFLAGS = -g
2.2.3 使用GCC编译C程序示例
以下给出使用GCC编译C程序示例。当前有两个源文件main.c和factorial.c,现在要编译生成一个计算阶乘的程序(因int类型大小的限制,读者不能输入太大的值),源程序如下:

// factorial.c源代码
#include <stdio.h>
#include <stdlib.h> 
int factorial (int n)    //计算数值n的阶乘
{
       if (n <= 1)
              return 1; 
       else
              return factorial (n - 1) * n;
}
//main.c源代码
#include <stdio.h>
#include <stdlib.h> 
int factorial (int n); 
int main (int argc, char **argv)
{
      int n; 
      if (argc < 2) {    //要求输入的参数有两个,一个为命令本身,另一个为数值
             printf ("Usage: %s \n", argv [0]);
             return -1;
      }
      else {
             n = atoi (argv[1]);  //将输入的第二个参数(字符类型)转换为数值以便计算
             printf ("Factorial of %d is %d.\n", n, factorial (n));
      } 
      return 0;
}

以下是按编译连接程序为可执行程序的步骤:

(1)编辑源代码。使用VIM等工具编辑源代码文件,完成后,文件信息如下:

[root@localhost ch0202]# ls   //查看源文件factorial.c和main.c,hello.C是在下
            //一个例子中用到的源程序,不予理会
factorial.c  hello.C  main.c

(2)使用gcc-c命令编译源代码(此步骤包含了-E对应的预处理操作,-S的汇编操作)。

[root@localhost ch0202]# gcc -c main.c   //编译main.c文件,生成main.o文件
[root@localhost ch0202]# ls    //查看main.o文件
factorial.c  hello.C  main.c  main.o
[root@localhost ch0202]# gcc -c factorial.c //编译factorial.c文件,生成factorial.o文件
[root@localhost ch0202]# ls    //查看factorial.o文件
factorial.c  factorial.o  hello.C  main.c  main.o

(3)使用gcc-o命令连接程序。

[root@localhost ch0202]# gcc -o factorial main.o factorial.o//连接成可执行程序factorial
[root@localhost ch0202]# ls       //查看factorial文件
factorial  factorial.c  factorial.o  hello.C  main.c  main.o

(4)执行程序。

[root@localhost ch0202]# ./factorial 3   //执行factorial程序,3为输入的变量
Factorial of 3 is 6.

另外,以上步骤也可以直接使用-o参数一次性完成。具体命令如下:

[root@localhost ch0202]# gcc -o factorial main.c factorial.c 
[root@localhost ch0202]# ./factorial 3   //执行factorial程序,3为输入的变量
Factorial of 3 is 6.

2.2.4 使用g++编译C++程序示例
GCC工具可用来同时编译C程序和C++程序。一般来说,编译器通过源文件的后缀名来判断是C程序还是C++程序(虽然Linux系统并不是通过后缀来判断文件)。在Linux中,C源文件的后缀名为.c(小写),而C++源文件的后缀名为.C(大写)、.cc或.cpp。

但是,gcc命令只能编译C++源文件,而不能自动和C++程序使用的库连接。因此,通常使用g++命令来完成C++程序的编译和连接,程序会自动调用gcc实现连接。

假设有一个如下的C++源文件hello.C:

#include <iostream.h> 
int main (int argc,char**argv)
{
            cout << "Hello, world!" << endl;
            return 0;
}

C++程序可以调用g++命令编译连接并生成可执行文件:

[root@localhost ch0202]# g++ -c hello.C  -Wno-deprecated //编译C++程序
            //-Wno-deprecated参数用于忽略头文件信赖的警告
[root@localhost ch0202]# ls       //查看编译后的.o文件
factorial  factorial.c  factorial.o  hello.C  hello.o  main.c  main.o
[root@localhost ch0202]# g++ -o hello hello.o   //连接程序
[root@localhost ch0202]# ls       //查看可执行文件hello
factorial  factorial.c  factorial.o  hello  hello.C  hello.o  main.c  main.o
[root@localhost ch0202]# ./hello       //执行程序
Hello, world!

同理,也可以使用-o参数一步完成编译连接操作。命令如下:

[root@localhost ch0202]# g++ -o hello hello.C -Wno-deprecated
[root@localhost ch0202]# ./hello 
Hello, world!

其中,-Wno-deprecated参数用于忽略头文件信赖的警告信息。

2.2.5 GDB演示示例
下面以一个简单的实例来演示GDB的调试方法。此示例程序源代码如下:

1 #include <stdio.h>
2 #include <stdlib.h>
3 static char buff [256];
4 static char* string;
5 int main ()
6{
7 printf ("Please input a string: ");
8 gets (string);       //从键盘获取字符串存入string中
9 printf ("\nYour string is: %s\n", string);
10}

这个程序很简单,目的是接受用户的输入,并将用户的输入打印出来。但是,程序的第8行使用了未初始化的字符指针string,因此,编译并运行之后,将出现段错误。

[root@localhost ch0204]# gcc -o bug -ggdb bug.c     //编译,并加上调试信息
/tmp/cckV4dCi.o(.text+0x36): In function `main':
bug.c: warning: the `gets' function is dangerous and should not be used.//已经做出警告
[root@localhost ch0204]# ./bug        //运行程序
Please input a string: hello        //要求输入字符
Segmentation fault          //段错误

下面利用GDB工具查找该程序中出现的问题,具体步骤如下。

(1)运行 gdb bug命令,装入bug可执行文件。

[root@localhost ch0204]# gdb bug
GNU gdb Red Hat Linux (6.3.0.0-1.21rh)    //版本
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.   //提示可以使用show copying命令
There is absolutely no warranty for GDB.  Type "show warranty" for details.
             //提示使用show warranty命令
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library /lib/libthread_db.so.1".

(2)使用list(可以使用l缩写)命令查看代码:

(gdb) l            //列出源代码信息
1     #include <stdio.h>
2     #include <stdlib.h> 
3     static char buff [256];
4     static char* string;
5     int main ()
6     { 
7           printf ("Please input a string: ");
8           gets (string); 
9           printf ("\nYour string is: %s\n", string);
10    }
(3)使用run命令执行程序:

(gdb) r            //执行程序
Starting program: /root/book/ch02/ch0204/bug 
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0x3ca000
Please input a string: hello       //输入字符
Program received signal SIGSEGV, Segmentation fault.  //提示错误
0x005607e0 in gets () from /lib/libc.so.6    //错误大致位置

(4)使用where命令查看程序出错位置:

(gdb) where           //查看出错位置
#0  0x005607e0 in gets () from /lib/libc.so.6   //gets()函数出错
#1  0x080483ea in main () at bug.c:8
(gdb) list           //列出出错位置代码
8         gets (string); 
9         printf ("\nYour string is: %s\n", string);
10    }

以上信息说明gets函数出错。从代码中可以看出,唯一能够导致gets函数出错的原因就是变量string。因此,使用print(可以简写成p)命令查看string变量:

(gdb) p string          //查看string变量
$1 = 0x0

使用quit命令退出GDB调试器:

(gdb) quit

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
友盟+U-APM 移动应用性能体验报告 :APM越发受到关注,第三方监控工具覆盖已超四成
近日,国内领先的全域数据智能服务商——友盟+,发布了《友盟+U-APM 移动应用性能体验报告》。据悉,友盟+于去年将原移动分析U-App错误分析模块正式升级为U-APM应用性能监控平台,经过近一年的观察,通过DEM 视角分析移动应用端的性能表现发布这份报告,旨在帮助开发者清晰了解行业动态,精准定位自身产品位置。
1129 0
《AngularJS高级程序设计》——5.11 小结
在本章中我提供了JavaScript语言的简单基础,以及AngularJS为补充核心语言特性而提供的实用方法。我还介绍了承诺以及AngularJS对JSON的支持,它们二者是使用Ajax并实现我在第3章中讲的单页面应用程序模型所必不可少的。
1298 0
《多核与GPU编程:工具、方法及实践》----2.2 PCAM方法学
PCAM代表分割(Partitioning)、通信(Communication)、聚集(Agglomeration)和映射(Mapping),是一个分四步的并行程序设计过程,由Ian Foster在其1995的书[34]中推广使用。
1682 0
《多核与GPU编程:工具、方法及实践》----第2章 多核和并行程序设计 2.1 引言
本章目标 学习设计并行程序的PCAM方法。 使用任务图和数据依赖图来识别可以并行执行的计算部分。 学习将问题的解法分解为可并发执行部分的流行的分解模式。 学习编写并行软件的主要程序结构模式,如主/从和fork/join。 理解分解模式的性能特点,如流水线。
993 0
TensorFlow新功能:TensorFlow Probability概率编程工具箱介绍
2018年,tensorflow开发者峰会上,tensorflow管理人员发布了:TensorFlow Probability——一种概率编程工具箱,用于机器学习研究人员和从业人员快速可靠地构建利用最先进硬件的复杂模型。快来学习一下吧~
3345 0
《JavaScript高级程序设计(第3版)》阅读笔记
第6章 面向对象的程序设计 6.2 创建对象 6.2.1 工厂模式 JavaScript创建对象(一)—— 工厂模式 6.
833 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
12049
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载