Linux程序设计——技术技巧与项目实践---C程序设计技术--第5章_第1页
Linux程序设计——技术技巧与项目实践---C程序设计技术--第5章_第2页
Linux程序设计——技术技巧与项目实践---C程序设计技术--第5章_第3页
Linux程序设计——技术技巧与项目实践---C程序设计技术--第5章_第4页
Linux程序设计——技术技巧与项目实践---C程序设计技术--第5章_第5页
已阅读5页,还剩78页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、2021-10-192021-10-191第五章 C程序设计技术5.1 管中窥豹学LINUX下的C编程5.2 GCC编译器应用解析5.3 GDB调试技术5.4 MAKE工程管理器5.6 AUTOTOOLS的应用5.7 LINUX下的C程序设计实践5.7 实战技巧 Linux启动模式与定时任务设置2021-10-192021-10-1925.1 管中窥豹学习C编程Linux下的下的C程序设计首先要确定开发环境,最实用的黄金组合就是程序设计首先要确定开发环境,最实用的黄金组合就是vim(emacs)+gcc+gdb。其中。其中vim(emacs)是编辑器,)是编辑器,gcc是多能编译器,是多能编译

2、器,gdb则是跟踪调试器。则是跟踪调试器。Gcc能够编译能够编译C、C+和和Object C等语言编写的程序,等语言编写的程序,还可以通过不同的前端模块来支持还可以通过不同的前端模块来支持Java、Fortran、Pascal、Modula 3和和Ada等各种语言。等各种语言。gdb是是GNU开发组织发布的功能强大的开发组织发布的功能强大的linux下的程序调试工具。在下的程序调试工具。在linux平台下做软件,平台下做软件,gdb比比VC、BCB的图形化调试器更强大。的图形化调试器更强大。编程固然可贵编程固然可贵 ,但调试价更高,本章旨在两者兼顾,既要熟悉编程方法,但调试价更高,本章旨在两者

3、兼顾,既要熟悉编程方法,又要掌握调试技巧,以保证程序顺利运行。下面指点读者牛刀小试。又要掌握调试技巧,以保证程序顺利运行。下面指点读者牛刀小试。Linux下的下的C源文件的编辑很简单,可以直接使用在其它环境中编辑的源文件的编辑很简单,可以直接使用在其它环境中编辑的*.c文件。文件。Linux下最常用的下最常用的C代码编辑器是代码编辑器是vi或或vim,其次是,其次是emacs编辑器。编辑器。第三章已经介绍了第三章已经介绍了vi编辑器,这里直接使用编辑器,这里直接使用emacs编辑器。编辑器。Linux下下C编程编程的基本过程是:输入源代码的基本过程是:输入源代码编译链接运行编译链接运行调试程序

4、。调试程序。2021-10-192021-10-193从最基本的 hello world 程序设计起步1输入源代码输入源代码$ emacs helloworld.c /进入进入emacs 编辑器环境编辑器环境#include /如果看不懂这个语句,请看看如果看不懂这个语句,请看看C语言设计书语言设计书int main( )printf(“Hello World.n”); / Emacs环境下按下环境下按下Tab有自动缩进功能有自动缩进功能exit(0);完成编辑后,按住完成编辑后,按住Ctrl+X,再按下,再按下Ctrl+C,提示是否保存时,按,提示是否保存时,按y退退出出 emacs编辑环境

5、,或直接点菜单保存文件。编辑环境,或直接点菜单保存文件。2021-10-192021-10-1942编译链接和运行程序编译链接和运行程序$ gcc helloworld.c o helloworld $ ./helloworld /./用于指明执行程序的路径用于指明执行程序的路径Hello World. /程序运行结果。过程如图程序运行结果。过程如图5.1所示。所示。这样在这样在linux平台上的简单平台上的简单C程序的开发与运行工作就完成了。程序的开发与运行工作就完成了。 2021-10-192021-10-1955.2 gcc编译器应用解析 编译器的工作是将源代码翻译成目标代码或机器语言,

6、在编译器的工作是将源代码翻译成目标代码或机器语言,在现代编译器中,一般分为两个阶段来实现。现代编译器中,一般分为两个阶段来实现。 第一阶段,编译器的前端接受输入的源代码,经过词法、第一阶段,编译器的前端接受输入的源代码,经过词法、语法和语义分析等得到源程序的某种中间代码。语法和语义分析等得到源程序的某种中间代码。 第二阶段,编译器的后端将前端处理生成的中间代码进行第二阶段,编译器的后端将前端处理生成的中间代码进行优化,最终生成在目标机器上可运行的代码。优化,最终生成在目标机器上可运行的代码。 Gcc提供了提供了30多条警告信息和多条警告信息和3个警告级别,有助于增强程个警告级别,有助于增强程序

7、的稳定性和可移植性。此外,序的稳定性和可移植性。此外,gcc还对标准的还对标准的C和和C+语语言进行了大量言进行了大量扩展扩展,提高了程序的执行效率,有助于代码,提高了程序的执行效率,有助于代码优化,减轻编程优化,减轻编程工作工作量。量。Gcc还提供了灵活强大的代码优还提供了灵活强大的代码优化功能,可以生成执行效率更高的代码。化功能,可以生成执行效率更高的代码。5.2.1 5.2.1 编译器的工作过程编译器的工作过程2021-10-192021-10-1965.2.2 gcc编译选项用用Gcc把把C语言源码文件生成可执行文件的过程要经历四个相互关联的步语言源码文件生成可执行文件的过程要经历四个

8、相互关联的步骤骤 预处理或称预编译预处理或称预编译Preprocessing、编译、编译Compilation、汇编、汇编Assembly和连和连接接Linking。这些过程的执行细节如下:。这些过程的执行细节如下:预处理或预编译,生成预处理或预编译,生成 *.i的文件的文件 预处理器预处理器cpp 将预处理后的文件转换成汇编语言,生成文件将预处理后的文件转换成汇编语言,生成文件 *.s 编译器编译器egcs 由汇编变为目标代码或机器代码,生成由汇编变为目标代码或机器代码,生成 *.o的文件的文件 汇编器汇编器as 连接目标代码,生成可执行程序连接目标代码,生成可执行程序 链接器链接器ld。

9、Gcc 命令的选项非常多,这里只列出针对命令的选项非常多,这里只列出针对C编译的常用选项:编译的常用选项: -o FILE 生成指定的输出文件。用在生成可执行文件时。生成指定的输出文件。用在生成可执行文件时。-g生成调试信息。生成调试信息。GNU 调试器可利用该信息。调试器可利用该信息。-c 只编译并生成目标文件。只编译并生成目标文件。-LDIRECTORY 指定额外的函数库搜索路径指定额外的函数库搜索路径DIRECTORY。 -lLIBRARY 连接时搜索指定的函数库连接时搜索指定的函数库LIBRARY。-O0 不进行优化处理。不进行优化处理。 -O2进一步优化。进一步优化。 -O3比比 -

10、O2 更进一步优化,包括更进一步优化,包括 inline 函数。函数。-w 不生成任何警告信息。不生成任何警告信息。 -Wall 生成所有警告信息。生成所有警告信息。 2021-10-192021-10-1975.2.3 gcc编译过程与游戏编程为了直观起见,图为了直观起见,图5.2列出了列出了gcc编译器的工作过程。编译器的工作过程。2021-10-192021-10-198如何让gcc为读者服务呢?只有一步步跟着做,才能理解编译过程的一鳞半爪,只有经过长期的只有一步步跟着做,才能理解编译过程的一鳞半爪,只有经过长期的学习,才能深谙编译的真谛。学习,才能深谙编译的真谛。对于上面列出的的对于上

11、面列出的的C程序,保存为程序,保存为helloworld.c后,最简单的编译操作后,最简单的编译操作是直接在命令行输入:是直接在命令行输入: #gcc helloworld.c在默认情况下,在当前文件夹下就会生成一个名为在默认情况下,在当前文件夹下就会生成一个名为a.out 的可执行文的可执行文件。输入如下命令就可以运行该可执行文件:件。输入如下命令就可以运行该可执行文件: $./a.outHello World.如果编辑新的文件如如果编辑新的文件如mytest.c,再用,再用gcc mytest.c直接编译后,还会产直接编译后,还会产生生a.out可执行文件将原文件覆盖。程序多了,读者就无法

12、知道是哪个可执行文件将原文件覆盖。程序多了,读者就无法知道是哪个程序创建了程序创建了a.out。此时,读者可以通过。此时,读者可以通过 -o选项,命名同源代码文件选项,命名同源代码文件同名的可执行文件。如新建文件为同名的可执行文件。如新建文件为game.c,则可以生成,则可以生成game.exe文件:文件:#gcc game.c -o game 2021-10-192021-10-199引导读者编写一个纸牌游戏到现在为止,读者已编译并运行了一个程序。每个初学编程者都想一到现在为止,读者已编译并运行了一个程序。每个初学编程者都想一下子编出下子编出1000行的程序,然后一次修改所有的错误,但这是痴

13、心妄想,行的程序,然后一次修改所有的错误,但这是痴心妄想,没有人能做到。读者要先写一个可以运行的小程序,修改运行,再逐没有人能做到。读者要先写一个可以运行的小程序,修改运行,再逐步添加、修改、再运行。这可以限制读者一次修改的错误数量。步添加、修改、再运行。这可以限制读者一次修改的错误数量。这里的头文件内容如下:这里的头文件内容如下:#ifndef DECK_H#define DECK_H#define DECKSIZE 52typedef struct deck_t int cardDECKSIZE; /* number of cards used */ int dealt;deck_t;#e

14、ndif /* DECK_H */2021-10-192021-10-1910本文件保存为本文件保存为deck.h。为了让头文件起作用,要把它加入到主程序之。为了让头文件起作用,要把它加入到主程序之中。可以在中。可以在game.c的第的第2行用行用#include deck.h调用这个头文件。在调用这个头文件。在第第5行写上行写上deck_t deck;,则,则game程序的主程序程序的主程序game.c代码如下:代码如下:#include #include deck.hint main() deck_t deck;shuffle(&deck); /*洗牌函数,后面将详细介绍洗牌函数,后面将详

15、细介绍*/printf(Hello World!n);2021-10-192021-10-1911解析本程序从预编译到运行结果的全过程 1预编译预编译 在在gcc预编译期间,会把预编译期间,会把deck.h文件内容复制到文件内容复制到game.c文件中。读者可文件中。读者可以通过在以通过在gcc后加上后加上 -E 选项来调用预编译器。选项来调用预编译器。 #gcc -E -o game _precompile.txt game.c#wc -l game _precompile.txt853 game _precompile.txt有有853行的输出。这其中大多数来自行的输出。这其中大多数来自s

16、tdio.h文件,但读者在查看这个文件,但读者在查看这个文件时,会发现结构体定义也在里面。如果读者不用文件时,会发现结构体定义也在里面。如果读者不用-o选项指定输出选项指定输出文件名,就输出到控制台。预编译过程完成了三个任务,一是把文件名,就输出到控制台。预编译过程完成了三个任务,一是把include文件拷贝到编译源文件中,二是用实际值替代文件拷贝到编译源文件中,二是用实际值替代define文本,三文本,三是在调用宏的地方进行了宏替换。是在调用宏的地方进行了宏替换。 这就使读者能够在整个源文件中使用符号常量,用这就使读者能够在整个源文件中使用符号常量,用DECKSIZE表示一表示一付牌中的纸牌

17、数量,如果它的值发生变化,所有使用它的地方都自动付牌中的纸牌数量,如果它的值发生变化,所有使用它的地方都自动更新。更新。2021-10-192021-10-19122编译编译作为一个中间步骤,作为一个中间步骤,gcc把所有代码翻译成汇编语言,通过分析代码把所有代码翻译成汇编语言,通过分析代码了解编程的目的。如果有语法错误,就在屏幕上显示出来,这样编译了解编程的目的。如果有语法错误,就在屏幕上显示出来,这样编译就失败了。有时这一步被误解为整个过程。实际上这只是冰山一角。就失败了。有时这一步被误解为整个过程。实际上这只是冰山一角。3汇编汇编 as把汇编代码转换为目标代码。事实上目标代码并不能在把汇

18、编代码转换为目标代码。事实上目标代码并不能在CPU上运行。上运行。编译器选项编译器选项 -c 把把 *.c 文件转换为以文件转换为以 *.o的目标文件。的目标文件。 如果读者运行如果读者运行 gcc -c game.c就自动生成名为就自动生成名为game.o的文件。的文件。下面继续编写游戏代码。我们把一付牌定义为下面继续编写游戏代码。我们把一付牌定义为deck_t,则读者要写一,则读者要写一个洗牌函数。这个函数接受一个指向个洗牌函数。这个函数接受一个指向deck类型的指针,并把一付随机类型的指针,并把一付随机的牌装入的牌装入deck类型。它用类型。它用drawn数组跟踪记录那些已经用过的牌。这

19、数组跟踪记录那些已经用过的牌。这个具有个具有DECKSIZE个元素的数组可以防止读者重复使用同一张牌。个元素的数组可以防止读者重复使用同一张牌。 2021-10-192021-10-1914把这个文件保存为把这个文件保存为shuffle.c。在这个代码中加入了一条调试语句,以便运行。在这个代码中加入了一条调试语句,以便运行时,能输出产生的牌号。因为游戏还在初级阶段,没有别的办法确定函数是时,能输出产生的牌号。因为游戏还在初级阶段,没有别的办法确定函数是否实现了读者要求的功能。使用一条否实现了读者要求的功能。使用一条printf语句,就能准确地知道现在究竟发语句,就能准确地知道现在究竟发生了什么

20、,以便在开始下一阶段之前我们知道牌已经洗好了。这种调试技术生了什么,以便在开始下一阶段之前我们知道牌已经洗好了。这种调试技术看起来很简单,但它用最少的语句完成了调试任务。看起来很简单,但它用最少的语句完成了调试任务。注意两个问题注意两个问题:一是读者用值传递方式传递参数,把变量的机器地址传递给了一是读者用值传递方式传递参数,把变量的机器地址传递给了函数,因此函数自己就能改变变量的值。也可以使用全局变量,尽量少用。函数,因此函数自己就能改变变量的值。也可以使用全局变量,尽量少用。 读者在一个新的读者在一个新的 *.c文件中用函数调用。操作系统总是寻找名为文件中用函数调用。操作系统总是寻找名为ma

21、in的函数,的函数,并从那里开始执行。并从那里开始执行。 shuffle.c中没有中没有main函数,因此不能编译为独立的可函数,因此不能编译为独立的可执行文件。读者必须把它与另一个具有执行文件。读者必须把它与另一个具有main函数并调用函数并调用shuffle的程序组合的程序组合起来。起来。 运行命令:运行命令: #gcc -c shuffle.c编辑编辑game.c文件,在第文件,在第7行,在行,在 deck_t类型的变量类型的变量deck声明之后,加上下面这声明之后,加上下面这一行:一行: shuffle(&deck);2021-10-192021-10-1915现在,如果读者象以前一样

22、创建可执行文件,就会出现一个错误:现在,如果读者象以前一样创建可执行文件,就会出现一个错误:#gcc -o game game.c/tmp/ccmiHnJX.o: In function main:/tmp/ccmiHnJX.o(.text+0 xf): undefined reference to shufflecollect2: ld returned 1 exit status为什么有这样的错误呢?因为编译器不知道为什么有这样的错误呢?因为编译器不知道shuffle函数在哪里。下面函数在哪里。下面就说明如何告诉编译器找到这个函数。就说明如何告诉编译器找到这个函数。2021-10-1920

23、21-10-19164连接连接器连接器ld。用下面的命令把由。用下面的命令把由as创建的目标文件转换为可执行文件:创建的目标文件转换为可执行文件: #gcc -o game game.o shuffle.o这时,两个目标文件组合起来创建了可执行文件这时,两个目标文件组合起来创建了可执行文件game。如果提示没。如果提示没有有game.o文件,就执行文件,就执行#gcc c game.c产生这个文件。产生这个文件。 这时连接器就从这时连接器就从shuffle.o目标文件中找到了目标文件中找到了shuffle 函数,把它并入可函数,把它并入可执行文件。目标文件的好处在于,读者想用哪个函数,直接包含

24、执行文件。目标文件的好处在于,读者想用哪个函数,直接包含deck.h文件并把文件并把shuffle.o目标文件连接到新的可执行文件中即可。目标文件连接到新的可执行文件中即可。 这样的代码重用经常发生。虽然读者没有编写前面作为调试语句调用这样的代码重用经常发生。虽然读者没有编写前面作为调试语句调用的的printf 函数,连接器却能从读者的函数,连接器却能从读者的#include 语句包含的文语句包含的文件中找到它的声明,并把存储在件中找到它的声明,并把存储在C库(库(/lib/libc.so.6)中的目标代码连)中的目标代码连接起来。接起来。2021-10-192021-10-19175两个重要

25、选项-Wall 选项可以打开所有类型的语法警告,帮助读者确定代码的正确选项可以打开所有类型的语法警告,帮助读者确定代码的正确性,并实现移植。当用这个选项编译代码时,或许会看到下述警告:性,并实现移植。当用这个选项编译代码时,或许会看到下述警告: game.c:7: warning: implicit declaration of function shuffle这表明程序还不是十全十美,需要在头文件中加入一行代码,告诉编这表明程序还不是十全十美,需要在头文件中加入一行代码,告诉编译器译器shuffle函数的细节并做检查。这样可以把函数的定义与实现分离函数的细节并做检查。这样可以把函数的定义与实

26、现分离开来,使读者能在任何地方使用读者的函数,只要包含新的头文件并开来,使读者能在任何地方使用读者的函数,只要包含新的头文件并连接到目标文件中就可以了。下面就把这一行加入连接到目标文件中就可以了。下面就把这一行加入deck.h中。中。 void shuffle(deck_t *pdeck);这就消除了那条警告信息。这就消除了那条警告信息。 另一个选项是优化选项另一个选项是优化选项 -O# (即即 -O2)。 告诉编译器代码要优化的级别。告诉编译器代码要优化的级别。编译器具有一整套技巧使读者代码运行更快。小程序无差别,但大程编译器具有一整套技巧使读者代码运行更快。小程序无差别,但大程序可以大幅提

27、高运行速度。序可以大幅提高运行速度。 2021-10-192021-10-19186调试我们知道,代码编译通过并不意味着它就完全正确。调试用调试器我们知道,代码编译通过并不意味着它就完全正确。调试用调试器gdb检查代码的可靠性。也可以用检查代码的可靠性。也可以用KDE提供的工具提供的工具KDbg。编译器选。编译器选项项-g可以把必要的调试信息加入目标文件。调试是一门经验型学问,可以把必要的调试信息加入目标文件。调试是一门经验型学问,也是程序员必须掌握的重要技术,值得读者花大量的时间学习与实践。也是程序员必须掌握的重要技术,值得读者花大量的时间学习与实践。 7 7程序的运行结果程序的运行结果经过

28、以上的过程,可以在经过以上的过程,可以在cygwin中运行程序,得到图中运行程序,得到图5.3所示的结果。所示的结果。2021-10-192021-10-19195.3 gdb调试技术Gdb是自由软件联盟的主要调试工具,用于被调试程序在执行过程中的内部是自由软件联盟的主要调试工具,用于被调试程序在执行过程中的内部活动情况,协助程序员找出代码错误。喜欢图形界面如活动情况,协助程序员找出代码错误。喜欢图形界面如VC、BCB等等IDE调试调试的读者,如果在的读者,如果在linux平台下做软件,会发现平台下做软件,会发现gdb这个调试工具有比图形化调这个调试工具有比图形化调试器有更强大的功能。试器有更

29、强大的功能。Gdb调试器应用的基本格式是:调试器应用的基本格式是:gdb -help -nx -q -batch -cd=dir -f -b bps -tty=dev -s symfile -e prog -se prog -c core -x cmds -d dir progcore|procID一般来说,一般来说,gdb主要帮忙读者完成下面四个基本任务:主要帮忙读者完成下面四个基本任务: 启动读者的程序。可以按照读者自定义要求随心所欲地运行程序。启动读者的程序。可以按照读者自定义要求随心所欲地运行程序。让被调试程序在读者指定断点处停止,断点可以是条件表达式。让被调试程序在读者指定断点处停止

30、,断点可以是条件表达式。当程序被停止时,可以检查读者程序中所发生的故事。当程序被停止时,可以检查读者程序中所发生的故事。动态改变读者程序的执行环境。动态改变读者程序的执行环境。启动启动gdb的方法有以下三种的方法有以下三种: gdb :program就是在当前目录下的可执行文件名。就是在当前目录下的可执行文件名。gdb core:用:用gdb同时调试一个运行程序和同时调试一个运行程序和core文件,文件,core是程序是程序非法执行后非法执行后core dump后产生的文件。后产生的文件。gdb :如果的程序是一个服务程序,那么可以指定这个服:如果的程序是一个服务程序,那么可以指定这个服务程序

31、运行时的进程务程序运行时的进程ID。gdb会自动会自动attach并调试。并调试。program应该在应该在PATH环环境变量中搜索得到。境变量中搜索得到。2021-10-192021-10-19205.3.1 调试编译与选项启动启动gdb后就进入了调试环境,用后就进入了调试环境,用gdb命令开始调试程序。在系统提命令开始调试程序。在系统提示符下输入命令示符下输入命令gdb后回车,就出现后回车,就出现gdb的版本号等软件基本信息,的版本号等软件基本信息,然后自动进入待命状态,等待读者的操作使用。然后自动进入待命状态,等待读者的操作使用。gdb的命令可以使用的命令可以使用help命令来查看,如图

32、命令来查看,如图5.4所示。所示。gdb的命令很多,的命令很多,help命令将列出其所有种类,包括命令别名、断点命令将列出其所有种类,包括命令别名、断点定义、数据查看、文件指定与检查、维护命令、模糊特性、程序执行、定义、数据查看、文件指定与检查、维护命令、模糊特性、程序执行、栈检查、状态查看、支持工具、程序在线跟踪执行和读者自定义命令栈检查、状态查看、支持工具、程序在线跟踪执行和读者自定义命令共共12类。若要看命令细节,用类。若要看命令细节,用help 命令如:命令如:help breakpoints查看断点设置的所有命令。也可以直接查看断点设置的所有命令。也可以直接help 来查看命令来查看

33、命令的帮助。的帮助。在在gdb中输入命令时,不用输入命令全名,只要输入前几个字符,但中输入命令时,不用输入命令全名,只要输入前几个字符,但这几个字符应能唯一标志一个命令。在这几个字符应能唯一标志一个命令。在linux下,读者可以敲击两次下,读者可以敲击两次TAB键补齐命令全称,如有重复的命令,键补齐命令全称,如有重复的命令,gdb会把它们都列出来。会把它们都列出来。 2021-10-192021-10-1921gdb的命令可以使用help命令查看2021-10-192021-10-19225.3.2 程序调试设置当以当以gdb 方式启动方式启动gdb后,后,gdb会在会在PATH路径和当前目录

34、路径和当前目录中搜索中搜索源文件。若要确认源文件。若要确认gdb是否读到源文件,可用是否读到源文件,可用l或或list查看查看gdb是否能列出源代码。在是否能列出源代码。在gdb中,运行程序用中,运行程序用r或或run命令。程命令。程序运行前要进行设置:序运行前要进行设置:1程序运行参数。用程序运行参数。用set args可指定运行时参数。如可指定运行时参数。如set args 10 20 30 40 50,show args可以查看设置好的运行参数。可以查看设置好的运行参数。2运行环境。用运行环境。用path 设定程序的运行路径,设定程序的运行路径,show paths查看程查看程序的运行路

35、径,序的运行路径,set environment varname =value设置环境变量如设置环境变量如set env USER=yuym。show environment varname 查看环境变量。查看环境变量。3工作目录用工作目录用cd ,用,用pwd 显示当前的所在目录。显示当前的所在目录。4程序的输入输出。用程序的输入输出。用info terminal显示读者程序用到的终端的模式。显示读者程序用到的终端的模式。用重定向控制程序输出。如:用重定向控制程序输出。如:run outfile。tty命令只写输入输出的命令只写输入输出的终端设备。如:终端设备。如:tty /dev/ttyb

36、。2021-10-192021-10-19235.3.3 在线调试过程gdb调试的功能强大,在许多情况下命令行调试工具有着图形化工具所调试的功能强大,在许多情况下命令行调试工具有着图形化工具所不能完成的功能。下面结合实例解析在线调试过程。不能完成的功能。下面结合实例解析在线调试过程。2021-10-192021-10-1924注意程序源码里不包含左侧的序号注意程序源码里不包含左侧的序号,去掉序号后编译生成执行文件。,去掉序号后编译生成执行文件。 yuym/test gcc -g sumpro.c -o sumpro使用使用gdb调试:调试:yuym/test gdb sumpro - 启动启动

37、gdbGNU gdb Red Hat Linux (5.3post-0.20021129.18rh)Copyright 2003 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type show copying to see the conditions.There is abs

38、olutely no warranty for GDB. Type show warranty for details.This GDB was configured as i386-redhat-linux-gnu.2021-10-192021-10-1925(gdb) l - l命令相当于命令相当于list,从第一行开始列出源码。,从第一行开始列出源码。1 #include 23 int func(int n)4 5 int sum=0,i;6 for(i=0; in; i+)7 8 sum+=i;9 10 return sum;(gdb) - 直接回车表示,重复上一次命令直接回车表示,重

39、复上一次命令11 12 main( )13 14 int i;15 long result = 0;16 for(i=1; i=100; i+)17 18 result += i;(gdb) break 15 - 设置断点,在源程序第设置断点,在源程序第16行处。行处。Breakpoint 1 at 0 x8048496: file sumpro.c, line 15.(gdb) break func - 设置断点,在函数设置断点,在函数func()入口处。入口处。Breakpoint 2 at 0 x8048456: file sumpro.c, line 5.(gdb) info brea

40、k - 查看断点信息。查看断点信息。Num Type Disp Enb Address What1 breakpoint keep y 0 x08048496 in main at sumpro.c:152 breakpoint keep y 0 x08048456 in func at sumpro.c:5(gdb) r - 运运行程序,行程序,run命令命令简写简写Starting program: /home/yuym/test/sumproBreakpoint 1, main () at sumpro.c:15 - 在在断断点点处暂处暂停。停。15 long result = 0;(g

41、db) n - 单单步步执执行下一行下一条语条语句,句,next命令命令简写简写。16 for(i=1; i=100; i+)(gdb) n18 result += i;(gdb) n16 for(i=1; i=100; i+)(gdb) n18 result += i;(gdb) c - 继续运继续运行程序,行程序,continue命令命令简写简写。Continuing.result1-100 = 5050 -程序程序输输出。出。Breakpoint 2, func (n=250) at sumpro.c:55 int sum=0,i;5 int sum=0,i;(gdb) n6 for(i

42、=1; i=n; i+)(gdb) p i - 显显示示变变量量i的的值值,print命令命令简写简写。$1 = 134513808(gdb) n8 sum+=i;(gdb) n6 for(i=1; i=n; i+)(gdb) p sum$2 = 1(gdb) n8 sum+=i;(gdb) p i$3 = 2(gdb) n6 for(i=1; i=n; i+)(gdb) p sum$4 = 3(gdb) bt - 查查看函看函数数堆堆栈栈。#0 func (n=250) at sumpro.c:5#1 0 x080484e4 in main () at sumpro.c:24#2 0 x40

43、0409ed in _libc_start_main () from /lib/libc.so.6(gdb) finish - 退出函退出函数数。Run till exit from #0 func (n=250) at sumpro.c:50 x080484e4 in main () at sumpro.c:1922 printf(result1-250 = %d n, func(250) );Value returned is $6 = 31375(gdb) c - 继续运继续运行。行。Continuing.result1-250 = 31375 -程序程序输输出。出。Program ex

44、ited with code 027. -程序退出,程序退出,调试结调试结束。束。(gdb) q gcc -g hello.c -o hello g+ -g hello.cpp -o hello 如果没有如果没有-g,读者就看不见程序的函数名、变量名,代替的全是运行,读者就看不见程序的函数名、变量名,代替的全是运行时的内存地址。在时的内存地址。在gdb环境中,读者可执行环境中,读者可执行linux的的shell命令,如:命令,如:$shell 。此时执行的是环境变量中定义的。此时执行的是环境变量中定义的shell。 在在gdb中,读者可以有几种暂停方式:断点中,读者可以有几种暂停方式:断点(B

45、reakPoint),观察点,观察点(WatchPoint),捕捉点,捕捉点(CatchPoint),信号,信号(Signals),线程停止,线程停止(Thread Stops)。如果要恢复程序运行,可以使用。如果要恢复程序运行,可以使用c或是或是continue命令。命令。有了以上的实战经验,现在可以系统把握有了以上的实战经验,现在可以系统把握gdb的性能。的性能。2021-10-192021-10-19315.3.4 在gdb中查看源程序gdb 可以显示被调试程序的源代码,但要求在编译程序时一定要加上可以显示被调试程序的源代码,但要求在编译程序时一定要加上-g参数,把源程序信息编译到执行文

46、件中。当程序停止运行时,参数,把源程序信息编译到执行文件中。当程序停止运行时, gdb会报告程序停在文件的第几行上。可以用会报告程序停在文件的第几行上。可以用list命令来显示程序的源代命令来显示程序的源代码。码。list : 显示程序第显示程序第linenum行的周围的源程序。行的周围的源程序。list : 显示函数名为显示函数名为function的函数的源程序。的函数的源程序。list : 显示当前行后面的源程序。显示当前行后面的源程序。list -:显示当前行前面的源程序。:显示当前行前面的源程序。一般显示当前行的上下一般显示当前行的上下5行,默认行,默认10行。也可以定制显示设置的行数

47、。行。也可以定制显示设置的行数。set listsize : 设置一次显示源代码的行数。设置一次显示源代码的行数。show listsize:查看当前:查看当前listsize的设置。的设置。1显示源代码显示源代码2021-10-192021-10-19322搜索源代码搜索源代码gdb提供了源代码搜索的命令:提供了源代码搜索的命令:forward -search search :向前面搜索。:向前面搜索。reverse -search :全部搜索。:全部搜索。其中,其中,是正则表达式。是正则表达式。3指定源文件路径指定源文件路径有时用有时用-g编译过后的执行程序中只包括源文件名而没有路径名。编

48、译过后的执行程序中只包括源文件名而没有路径名。gdb提供了指定源文件路径的命令,以便提供了指定源文件路径的命令,以便gdb进行搜索。进行搜索。directory 或或dir :加源文件路径到当前路:加源文件路径到当前路径前。如果指定多个路径可以使用径前。如果指定多个路径可以使用“:”,Windows下可以使用下可以使用“;”。directory: 清除所有的自定义的源文件搜索路径信息。清除所有的自定义的源文件搜索路径信息。show directories: 显示定义了的源文件搜索路径。显示定义了的源文件搜索路径。2021-10-192021-10-19334源代码的内存地址源代码的内存地址可以

49、用可以用info line命令来查看源代码在内存中的地址。命令来查看源代码在内存中的地址。info line后可以跟后可以跟“行号行号”,“函数名函数名”,“文件名文件名:行号行号”,“文件名文件名:函数名函数名”,从,从而显示指定的源码在运行时的内存地址,如:而显示指定的源码在运行时的内存地址,如:(gdb) info line tst.c:funcLine 5 of tst.c starts at address 0 x8048456 and ends at0 x804845d .disassemble可查看源程序当前执行时的机器码,并把内存中指令可查看源程序当前执行时的机器码,并把内存中

50、指令dump出来。如图出来。如图5.5显示了查看函数显示了查看函数func的汇编代码。的汇编代码。(gdb) disassemble func2021-10-192021-10-19345.3.5 在gdb中查看栈信息当程序停止时可以查看程序停在哪里。若程序调用了函数,则函数地址、参当程序停止时可以查看程序停在哪里。若程序调用了函数,则函数地址、参数、局部变量都会压入栈数、局部变量都会压入栈stack中。可以用中。可以用gdb查看当前栈信息。主要命令是:查看当前栈信息。主要命令是:Backtrace或或bt:显示当前的函数调用栈的所有信息。如:显示当前的函数调用栈的所有信息。如: (gdb)

51、bt#0 func (n=250) at tst.c:6#1 0 x08048524 in main (argc=1, argv=0 xbffff674) at tst.c:30#2 0 x400409ed in _libc_start_main () from /lib/libc.so.6从这里可以看出函数的调用栈信息。从这里可以看出函数的调用栈信息。backtrace 或或bt :n是正整数,表示只显示栈顶上是正整数,表示只显示栈顶上n层的栈信息。层的栈信息。backtrace 或或 bt :-n是负整数,只显示栈底下是负整数,只显示栈底下n层的栈信息。层的栈信息。frame 或或f :n

52、为为0开始的栈层号。开始的栈层号。frame 0栈顶,栈顶,frame 1栈第二层。栈第二层。up 表示向栈的上面移动表示向栈的上面移动n层,可以没有层,可以没有n,表示向上移动一层。,表示向上移动一层。down :表示向栈下移动:表示向栈下移动n层,没有层,没有n时表示向下移动一层。时表示向下移动一层。查看当前栈的信息,可以用以下查看当前栈的信息,可以用以下gdb命令:命令:frame或或f:显示信息包括栈层编号、当前函数名、函数参数值、函数所在文:显示信息包括栈层编号、当前函数名、函数参数值、函数所在文件及行号,函数执行到的语句。件及行号,函数执行到的语句。info frame或或info

53、 f:显示更为详细的当前栈层信息,但大多是内存地址。:显示更为详细的当前栈层信息,但大多是内存地址。info args:显示出当前函数的参数名及其值。:显示出当前函数的参数名及其值。info locals:显示出当前函数中:显示出当前函数中所有局部变量及其值。所有局部变量及其值。info catch:显示出当前的函数中的异常处理信息。:显示出当前的函数中的异常处理信息。 2021-10-192021-10-19355.3.6 在gdb中查看运行数据在程序调试时,可以用在程序调试时,可以用print或或p命令或同义命令命令或同义命令inspect来查看程序的运行数来查看程序的运行数据。据。pri

54、nt命令的格式是:命令的格式是:print 或或print / 其中其中是表达式,是被调试程序语言的表达式,是表达式,是被调试程序语言的表达式,是输出格式,如把是输出格式,如把表达式按表达式按16进制输出就是进制输出就是/x。1表达式。可以是当前程序运行中的表达式。可以是当前程序运行中的const常量、变量、函数等内容。常量、变量、函数等内容。2程序变量。可以随时查看全局变量、静态全局变量和局部变量的值。要查程序变量。可以随时查看全局变量、静态全局变量和局部变量的值。要查看全局变量值,可以用看全局变量值,可以用“:”操作符。如查看操作符。如查看f2.c的全局变量的全局变量x的值:的值:gdb)

55、 p f2.c:x如果程序编译时开启了优化选项,可能会发生某些变量不能访问或取值错误如果程序编译时开启了优化选项,可能会发生某些变量不能访问或取值错误的情况。这时在编译程序时建议关闭优化。的情况。这时在编译程序时建议关闭优化。3数组数组有时候要查看一段连续的内存空间的值。例如在程序中有这样的语句:有时候要查看一段连续的内存空间的值。例如在程序中有这样的语句:int *array = (int *) malloc (len * sizeof (int);在在gdb调试过程中,可以显示这个动态数组的取值:调试过程中,可以显示这个动态数组的取值:p *arraylen其中其中的左边是数组的首地址的值

56、,也就是变量的左边是数组的首地址的值,也就是变量array指向的内容,右边是数指向的内容,右边是数据长度,保存在变量据长度,保存在变量len中,其输出结果是:中,其输出结果是:(gdb) p *arraylen$1=2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,34, 36, 38, 40如果是静态数组可以直接用如果是静态数组可以直接用p数组名来显示数组中所有数据内容。数组名来显示数组中所有数据内容。2021-10-192021-10-19364输出格式输出格式可以使用可以使用gdb的数据显示格式:的数据显示格式:x 按十

57、六进制格式显示变量,按十六进制格式显示变量,d按十进制格式按十进制格式显示变量,显示变量,u 按十六进制格式显示无符号整型,按十六进制格式显示无符号整型,o按八进制格式显示变量,按八进制格式显示变量,t 按二进制格式显示变量,按二进制格式显示变量,a按十六进制格式显示变量,按十六进制格式显示变量,c按字符格式显示变量,按字符格式显示变量,f按浮点数格式显示变量。按浮点数格式显示变量。(gdb) p i$21 = 101(gdb) p /a i$22 = 0 x655查看内存查看内存可以用可以用examine或或x查看内存地址中的值。查看内存地址中的值。x命令的语法如下所示:命令的语法如下所示:

58、 x/ 其中其中n、f、u是可选参数。是可选参数。 n表示要显示内存的长度,是从当前地址向后显示表示要显示内存的长度,是从当前地址向后显示几个地址的内容。几个地址的内容。 f 是显示格式,可以是字符串是显示格式,可以是字符串s,或指令地址格式,或指令地址格式i。u表示表示从当前地址往后请求的字节数,如果不指定,从当前地址往后请求的字节数,如果不指定,gdb默认是默认是4个个bytes。u可以用可以用字符代替,字符代替,b表示单字节,表示单字节,h为双字节,为双字节,w四字节,四字节,g八字节。八字节。表示一个表示一个内存地址。内存地址。n/f/u三个参数可以一起使用。如:三个参数可以一起使用。

59、如:(gdb)x /3uh 0 x54320 表示,从内存地址表示,从内存地址0 x54320读取内容。读取内容。2021-10-192021-10-19376自动显示自动显示可以设置一些自动显示的变量,当程序停止或在单步跟踪时,这些变可以设置一些自动显示的变量,当程序停止或在单步跟踪时,这些变量将自动显示。相关的量将自动显示。相关的gdb命令是命令是display。 display 或或 display/ 或或 display/ 其中的其中的expr是表达式,是表达式,fmt表示显示的格式,表示显示的格式,addr表示内存地址,当表示内存地址,当用用display设定好一个或多个表达式后,只

60、要程序停下来,设定好一个或多个表达式后,只要程序停下来,gdb会自动会自动显示所设置的这些表达式的值。格式显示所设置的这些表达式的值。格式i和和s同样被同样被display支持,一个非支持,一个非常有用的命令是:常有用的命令是: display/i $pc$pc是是gdb的环境变量,表示指令的地址,的环境变量,表示指令的地址,/i表示输出格式为机器指令表示输出格式为机器指令码即汇编。当程序停下时出现源代码和机器码对应情形,这个很有意码即汇编。当程序停下时出现源代码和机器码对应情形,这个很有意思。思。2021-10-192021-10-19387设置显示选项设置显示选项gdb中关于显示的选项比较

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论