冒牌程序员-毛哥 C语言入门教程(第二章:数据类型 第四节 调试工具简介)

阅读 9

01-30 21:00

第二章:数据类型

第四节:调试工具简介


在谈论无符号整数前,我们先要熟悉一个工具,这个工具的名名字叫gdb。一般来说,这个工具我们叫做调试器。现在这么叫,大家可能不明白意思,我这么解释一下吧,这个gdb就像一个盒子,我们把我们编写的程序放到这个盒子里面运行,这个盒子又个神奇的功能,能够控制整个程序的运行,让程序运行,程序就运行,让程序停止运行,他就停止运行,并且还可以指定程序在那个地方停止运行。另外,在程序停止运行期间,我们还可以通过这个工具查看这个程序运行的时候,程序内每个变量当前的值,也就是程序暂停的时候,每个变量的值。而且,还可以看你想看的内存地址中,所保存的二进制串。这个工具牛逼吧。


这个工具咋用呢?我们先编写一下程序:

/*
冒牌程序员-毛哥
*/
/***************debug_1.c**********/

#include<stdio.h>
int main()
{
    int x,y;
    x=20;
    y=30;
    printf("x=%d y=%d\n",x,y);
    x+=100;
    y=0x12345678;
    return 0;
}

按照下图中,输入黄色渲染的命令,就会出现如下画面:

冒牌程序员-毛哥 C语言入门教程(第二章:数据类型 第四节 调试工具简介)_可执行文件


看上图中被黄色荧光笔标注的部分,就是我们要输入的命令或者选项。

第一条:

gcc debug_1.c -o 1 -g


这里,gcc是编译命令,debug_1.c是源代码,-o 1是说明生成的可执行文件名字是1。-g说明在生成的可执行文件中,应该包含可执行文件的调试信息。

说明:

包含调试信息这句话对与初学者来说比较抽象,到底啥是调试信息,啥是调试呢?调试其实就是查找程序中的错误,也就是你们常听说的找bug。怎么找呢,就是在程序员认为可能出错的地方,把程序暂停,然后看程序中各个变量或者其他信息,是不是程序执行到这里,按照程序逻辑应该有的信息,如果发现信息不对,就查找原因,这就是调试。为了完成上述目的,需要一些辅助信息。首先,我们可以将程序暂停在源代码中的某一句话上,例如,我们需要将程序暂停到return 0;这句话上(暂停到这句话,其实此时这句话还没有被执行,而这句话的上一句y=0x12345678;已经被执行完毕)。如何暂停呢,我们就使用调试器命令“b n”,其中b是调试命令,n是参数,n代表的是程序应该暂停的行数,也就是行号。那么这个时候,调试器必须知道return 0;这句话,被翻译成了那些机器执行,当执行到这些机器指令前,必须暂停。这个机器指令和语言源代码对应关系信息是不是调试器必须知道呢?肯定是必须的,这些信息对于不需要调试的程序是否有价值呢,没有,因为正常程序不会被暂停。那么这个二进制指令和源代码对应关系信息就是调试信息,-g编译选项就是要编译器生成这些信息,然后把这些信息以一定的格式,保存在可执行文件中。gdb调试器在调试可执行程序的时候,就可以使用这些信息了。当然,调试信息不只包含源代码和二进制指令对应关系,还有其他很多东西,专门有技术标准进行规定。


第二条:

gdb 1

这里gdb是调试器启动命令,后面的1是参数,代表要调试的目标程序。

第三条:

start

这个其实已经是一条调试器的指令了,表示被调试程序开始运行。

第四条:

n

程序开始调试的时候,调试器会检测到可执行程序1中,有些机器指令没有对应的调试信息,此时调试器会问你,是否要让调试器自动下载可执行程序没有的调试信息(至于从哪里下载,调试器是知道的,从上面截图中可以看出,是在一个网站上https://debuginfod.ubuntu.com)。这里我们只调试我们自己写的代码,因此没有必要下载其他的调试信息,因此选了不下载。

然后调试器会将程序暂停在main函数的第一据可执行代码上。在截图中,可以看出breakpoint 1,main()at debug_1.c:10。啥意思呢?就是第一个断点,在main函数中,main函数在debug_1.c这个源文件中,暂停在源文件的第10行。

同时可以看见,10 x=20;这一行,这一行表示被暂停的行号10和这行的代码 x=20;(注意,此时这行还没有被执行)。

然后这个时候,我们希望程序暂停在return 0;这一句源代码上,这样我们就可以查看y的值以及和y相关的其他信息。

冒牌程序员-毛哥 C语言入门教程(第二章:数据类型 第四节 调试工具简介)_可执行文件_02

为了达到上述目的,我们输入 b 15。

(return 0; 在第15行,b表示breakpoint断点的意思)

回车后出现断点设置成功的信息。然后我们输入c回车。(c表示continue的意思,就是继续执行的意思,因为我们在第15行设置了断点,那么程序继续执行,到第15行被暂停了下来,如上图,显示 breakpoint 2, main() at debug_1.c:15。同时显示第十五行的源代码 return 0;暂停在这里后,调试器等待我们输入下一个调试命令。

我们在这里暂停的目的是查看y的值,以及y这个变量所在内存中,二进制序列的情况。如何查看呢?

首先,我们用p y来查看y的值:

冒牌程序员-毛哥 C语言入门教程(第二章:数据类型 第四节 调试工具简介)_可执行文件_03

p表示print的意思,y是变量的名字。合起来就是打印y变量的值。可以用计算机算一下,0x12345678是不是就是305419896。如果不是,肯定程序哪里有问题了,如果是就表明没有问题。

现在我们还想看一下,y变量所在内存中,二进制序列的情况。

冒牌程序员-毛哥 C语言入门教程(第二章:数据类型 第四节 调试工具简介)_调试器_04

命令x的用法如下:

x/nfu 地址
    
		n-内存单元个数
	
    f:
          x	16进制
          d	10进制
          u	10进制无符号
          o	8进制
          t	二进制
          a	16进制
          i	执行格式
          c	字符格式
          f	浮点数格式


    u:
          b	单字节
          h	双字节
          w	四字节
          g	八字节

  x/4xb &y的意思是,4-显示四个单元,x-以十六进制的格式显示,b每个单元一个字节。回车后,显示如下:

0x7fffffffe02c: 0x78    0x56    0x34    0x12

0x7fffffffe02c是y变量的内存地址,以十六进制的形式显示。0x78是内存地址为0x7fffffffe02c的一个字节的二进制串,只不过是以十六进制的形式显示,这里我们没有必要将其翻译成二进制形式。以此类推,0x7fffffffe02d地址的字节中,二进制串是0x56,0x7fffffffe02e地址的字节中,二进制串是0x34...。

通过观察,我们会发现y的值是0x12345678,而内存中从低地址到高地址的二进制串是:0x78 56 34 12。正好顺序反了。在高地址处,存放的是32位整数的高位数字,而在低地址上,存放的是32位整数的低位数字。还有一种存储方法,就是高地址处,存放0x78,而低地址处,存放0x12,这样就更符合我们人类的阅读习惯了。两种方法都可以,但总得选一种吧。此时有的人就选第一种,违反人类阅读习惯的方法,有的人就选第二种,符合人类阅读习惯的。我们现在用的最多的是第一种,叫做小端表示法,就是我们现在这个环境中使用的方法。

那现在如何结束调试呢?输入quit,或者q回车,然后再输入y就可以了。

冒牌程序员-毛哥 C语言入门教程(第二章:数据类型 第四节 调试工具简介)_程序暂停_05



精彩评论(0)

0 0 举报