gdb调试教程

编程工具 专栏收录该内容
3 篇文章 0 订阅

gdb调试是c/c++代码调试最重要的工具了,我们今天来系统熟悉一下。

——————————————————————————————————————

太长不看版:

(注意,如果针对cmake项目,需要把编译模式从release改成debug,否则无法定位到准确的行数)

  • 进入gdb debugger界面==>gdb filename
  • 查看segmentation fault 位置:
    run
    backtrace
  • 加断点: break filename:line 
  • 加条件断点: break filename:line if (condition) 
  •  查看局部变量: print(variable)
  • 运行 run
  • 继续运行 continue
  • 运行下一个指令 next
  • 进入一个函数体 step into 
  • 观察一个变量 watch 

——————————————————————————————————————

手把手教学版

先上一段充满bug的代码:

#include <stdio.h>
#include <stdlib.h>
 // this function is 100% correctly implemented

void print_heart();
void times_two(int *num) {
  // calculate 2*num
  for (int i = 0; i < *num; i++) {
    *num++;
  }
}

 

 
int main(int argc, char *argv[]) {
  // parse arguments
  if (argc != 2) {
    printf("usage: %s <number>\n", argv[0]);
    return 1;
  }
   print_heart();
 
  // allocate memory
  int *number = malloc(sizeof(int));
  if (number = NULL)
    return 1;
  *number = atoi(argv[0]);

  // calcualte and print result
  times_two(number);
  printf("Result: %d\n", *number);

  // free memory
  free(number);
  return 0;
}

void print_heart() {
 
	int i, j;
	for (i = 0; i < 3;i++)
	{
		for (j = 0; j < 5-2*i;j++)
		{
			printf(" ");
		}
		for (j = 0; j < 5+4*i;j++)
		{
			printf("❤");
		}
		for (j = 0; j < 9-4*i;j++)
		{
			printf(" ");
		}
		for (j = 0; j < 5+4*i;j++)
		{
			printf("❤");
		}
		printf("\n");
    }
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 29;j++)
			printf("❤");
		printf("\n");
 
	}
	for (i = 0; i < 6+ 1; i++)
	{
		for (j = 0; j < 2*i+1; j++)
			printf(" ");
		for (j = 0; j < 27-4*i; j++)
			printf("❤");
		printf("\n");
	}
	for (i = 0; i <1; i++)
	{
		for (j = 0; j < 14; j++)
			printf(" ");
		for (j = 0; j < 1; j++)
			printf("❤");
		printf("\n");
	}
 

 
}

这个代码的作用是打印一个骚气的爱心,然后,将输入的数字×2.(显然,刚开始,代码不能实现这样的功能)

用gcc编译改代码:

gcc -g -std=c99 -o bug bug.c

#run
./bug 3

你会发现有core dump,所以,接下来开始debug

启动gdb

gdb --args ./bug 10

这时,你会发现,Terminal左边出现了(gdb)

然后输入“run”

从终端可以看到,在28行出现错误,然后对应代码;

28行涉及到的变量包括number,和右边的参数。我们可以用gdb显示变量的数值,所以来看看number吧。

通过检查,发现number指向了一个nullptr,这显然不是我们期望的。再来看看argv[0], 发现它是一个

我们希望的是把输入char改成int类型,但是,发现它不是这样的。所以,对代码修改。

重新编译,使用gdb

从结果上看,还是一样的错误。

但是,这时候如果打印会发现;

所以,右边的数值是我们期待的,去除了一个bug。。。只是左边还是一个nullptr。

因为程序一旦碰到bug结束,就要重新运行,所以调试的时候,我们希望一步一步,就需要加breakpoint。在之前的代码里,在28行出现了问题,所以我们需要在这之前添加断点,我们关注的变量是number,因此,我选择了在25行添加断点。

注意,断点打在25行意味着,25行还没有被执行

我们执行next,来跑下一行代码,对应的就是25行,这时候,可以发现,和我们的预期一样,number是一个有效地址。继续next,然后在28行,地址重新变为了0。 

所以问题其实就找到了。在26行的判断语句中,我们把number重新赋值为null。所以,赶紧修改, 重新编译

继续在28行打断点,然后next break bug.c 28

发现,*number = 10, 现在正常了

然后,自信的continue

但是,结果是不对的。。。重新输入run,next。

通过step,我们可以进入函数,

输入list,我们可以在终端查看代码

print i 会发现 i对应的是一个奇怪的数字,因为还没有初始化。

接着,next

这时候,print 就得到了 0

通过操作,我们发现,在for循环中,增加的是地址,而我们希望的是,增加地址对应的数的大小。

所以,修改代码。

接下来重新编译,然后这次我们在函数循环的开始处加断点,这时候,我们观察num对应的地址,没有改变,说明改变是生效可行的。

我们假设程序是正确的,直接continue,这时候,其实还是naive了。结果是错的。

重新run,继续debug,

我们在循环开始的地方加上断点,因为我们想知道进入循环之前的状态

我们加上一个条件断点,因为我们关心当在循环中满足条件时,为什么没有正常退出。我们关注×num最后一次做加法的情况,也就是×num=19==》后来测试发现,在下一行观察number和i似乎更有效。。。。

这时候,其实结果很明显了

懂得都懂,不懂的也不方便多说,这行水太深。我们对程序进行修改。

还有一个重要功能:

backtrace: 可以查看代码嵌套的过程。并且,非常适合找到segmentation对应的行数。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页

打赏作者

海滩游侠

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值