当前位置: 首页 > news >正文

C语言gdb调试

gdb调试

下面以一个计算数组元素之和的程序sum_array.c为例,逐个演示gdb调试命令的用法,帮助理解每个指令的实际作用:

#include<stdio.h>//计算数组元素之和
int calculate_sum(int arr[],int len){int sum = 0;for(int i = 0;i < len;i++){sum += arr[i];//第8行:累加元素}return sum;
}int main(void){int numbers[] = {10,20,30,40,50};int length = sizeof(numbers) / sizeof(numbers[0]);// 数组长度为5int total = calculate_sum(numbers,length);//第16行:调用求和函数printf("数组元素之和:%d\n",total);//第17行:输出结果return 0;
}

在这里插入图片描述

调试步骤与命令详解

1. 编译程序(带-g选项)

首先用-g选项编译程序,生成包含调试信息的可执行文件:

gcc sum_array.c -o sum_array -g

2. 启动gdb调试

输入gdb ./sum_array进入调试环境,前缀变为(gdb)表示就绪:

~$gcc sum_array.c -o sum_array -g	#编译程序
~$gdb ./sum_array					#启动gdb调试
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./sum_array...done.
(gdb) 

在这里插入图片描述

3. list(缩写l):查看代码

作用:查看源代码,默认每次显示10行。

l:显示从第一行开始的代码(每次按回车可继续显示下10行):

(gdb) l
1	#include<stdio.h>
2	
3	
4	//计算数组元素之和
5	int calculate_sum(int arr[],int len){
6	    int sum = 0;
7	    for(int i = 0;i < len;i++){
8	        sum += arr[i];//第8行:累加元素
9	    }
10	    return sum;

回车后显示下10行:

(gdb) 
11	}
12	
13	int main(void){
14	    int numbers[] = {10,20,30,40,50};
15	    int length = sizeof(numbers) / sizeof(numbers[0]);// 数组长度为5
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数
17	    printf("数组元素之和:%d\n",total);//第17行:输出结果
18	    return 0;
19	}

在这里插入图片描述

l 行号:查看指定行号为中心的代码(如查看第8行附近,共10行,以第8行为中心):

(gdb) l 8
3	
4	//计算数组元素之和
5	int calculate_sum(int arr[],int len){
6	    int sum = 0;
7	    for(int i = 0;i < len;i++){
8	        sum += arr[i];//第8行:累加元素
9	    }
10	    return sum;
11	}
12	

在这里插入图片描述

l 函数名:查看指定函数为中心的代码(如查看main函数附近的代码,共10行,以main函数为中心):

(gdb) l main
8	        sum += arr[i];//第8行:累加元素
9	    }
10	    return sum;
11	}
12	
13	int main(void){
14	    int numbers[] = {10,20,30,40,50};
15	    int length = sizeof(numbers) / sizeof(numbers[0]);// 数组长度为5
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数
17	    printf("数组元素之和:%d\n",total);//第17行:输出结果

在这里插入图片描述

4. break(缩写b):设置断点

作用:在指定位置设置断点,程序运行到断点处会暂停,方便观察中间状态。

b 行号:在指定行号设置断点(如在16行main函数调用calculate_sum处设断点):

(gdb) b 16
Breakpoint 1 at 0x40061c: file sum_array.c, line 16.

在这里插入图片描述

b 函数名:在指定函数的入口处设置断点(如在calculate_sum函数开头设断点):

(gdb) b calculate_sum
Breakpoint 2 at 0x4005a1: file sum_array.c, line 6.

在这里插入图片描述

5. run(缩写r):执行程序

作用:启动程序执行,直到遇到断点或程序结束。

(gdb) r
The program being debugged has been started already.   //第一行
Start it from the beginning? (y or n) y				   //第二行
Starting program: /home/tarena/sum_array 			   //第三行Breakpoint 1, main () at sum_array.c:16				   //第五行
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数   //第六行

在这里插入图片描述
由于我第一次已经使用了r,第二次使用r的时候会显示是否重新执行程序

  • 第一行:The program being debugged has been started already.:意思是当前调试的程序已经启动过一次了。说明你之前已经执行过rrun)命令启动程序,现在再次输入r,gdb会提示你是否重新开始。
  • 第二行:Start it from the beginning? (y or n) y:意思是是否从程序开头重新启动?(输入y或n)。你输入了y(yes),表示同意重新启动程序。
  • 第三行:Starting program: /home/tarena/sum_array:意思是正在启动程序,路径为/home/tarena/sum_arraygdb按照你的指令,重新从程序开头开始执行。
  • 第五行:Breakpoint 1, main () at sum_array.c:16:意思是程序在“断点1”处暂停,当前位于main函数,文件sum_array.c16行。说明你之前在第16行设置过断点(b 16),程序重新启动后,执行到这里触发了断点,因此暂停。
  • 第六行:16 int total = calculate_sum(numbers,length);//第16行:调用求和函数:意思是当前暂停的位置,是第16行代码。这一行是“下一步要执行的代码”(还未执行),gdb会显示这一行,方便你确定当前执行位置。

6. next(缩写n):单步执行(不进入函数)

作用:执行当前行代码,然后暂停(若当前行时函数调用,不进入函数内部,直接执行完函数并暂停)。

(gdb) nBreakpoint 2, calculate_sum (arr=0x7fffffffde00, len=5) at sum_array.c:6  //第一行
6	    int sum = 0;													  //第二行

在这里插入图片描述

  • 第一行:Breakpoint 2, calculate_sum (arr=0x7fffffffde00, len=5) at sum_array.c:6:由于calculate_sum函数内部第 6 行设有断点(Breakpoint 2),程序执行到该函数时触发断点,暂停在函数入口附近。其中:
    • arr=0x7fffffffde00:数组numbers的内存地址(传给形参arr的实参)。
    • len=5:数组长度(传给形参len的实参,通过sizeof计算得到)。
  • 第二行:6 int sum = 0;:当前暂停的位置,是calculate_sum函数的第 6 行,这一行是下一步要执行的代码(还未执行)。

7. step(缩写s):单步执行(进入函数)

作用:执行当前行代码,若当前行是函数调用,会主动进入函数内部,并在函数第一行有效代码处暂停。

现在重新启动程序,在第16行断点处执行s

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/tarena/sum_array Breakpoint 1, main () at sum_array.c:16
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数
(gdb) sBreakpoint 2, calculate_sum (arr=0x7fffffffde00, len=5) at sum_array.c:6   //第一行
6	    int sum = 0;													   //第二行

在这里插入图片描述

  • 第一行:Breakpoint 2, calculate_sum (arr=0x7fffffffde00, len=5) at sum_array.c:6:由于calculate_sum函数内部第 6 行设有断点(Breakpoint 2),程序执行到该函数时触发断点,暂停在函数入口附近。其中:
    • arr=0x7fffffffde00:数组numbers的内存地址(传给形参arr的实参)。
    • len=5:数组长度(传给形参len的实参,通过sizeof计算得到)。
  • 第二行:6 int sum = 0;:当前暂停的位置,是calculate_sum函数的第 6 行,这一行是下一步要执行的代码(还未执行)。

重点:区分ns

对于n(next):当在调用函数的行(如第16行)执行n时,它的核心逻辑是“不进入被调用函数内部”,会直接执行完整个函数调用语句。但如果被调用函数内部本身设有断点(如calculate_sum的第6行),那么程序会先执行完函数调用的“进入过程”,然后因为函数内部的断点被触发而“被动暂停”在断点处。这里的暂停本质是断点的强制作用,而n本身并不主动控制是否进入函数,只是按“不进入内部单步”的规则执行,最终因断点被动停下。

对于s(step):当在调用函数的行(如第16行)执行s时,它的核心逻辑是“主动进入被调用函数内部”,会跟踪到函数的第一行有效代码。如果此时函数内部恰好有断点(如calculate_sum的第6行),那么进入函数后会因断点而暂停;即使函数内部没有断点,s也会主动进入函数并在第一行有效代码处暂停(此时的暂停是s主动进入后的自然停留,而非依赖断点)。

故:

n是被动暂停

s是主动进入后暂停

显著区分的操作:

  1. 首先查看已经存在的断点信息:

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040061c in main at sum_array.c:16breakpoint already hit 1 time
2       breakpoint     keep y   0x00000000004005a1 in calculate_sum at sum_array.c:6
  1. 删除第一个断点:

delete 2

(gdb) delete 2
  1. 查看删除之后的断点信息:

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040061c in main at sum_array.c:16breakpoint already hit 1 time

现在再次执行ns看一看区别:

  • 3.1 重新启动程序然后执行n:

r

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/tarena/sum_array Breakpoint 1, main () at sum_array.c:16
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数

n

(gdb) n
17	    printf("数组元素之和:%d\n",total);//第17行:输出结果
  • 3.2 重新启动程序然后执行s

r

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/tarena/sum_array Breakpoint 1, main () at sum_array.c:16
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数

s

(gdb) s
calculate_sum (arr=0x7fffffffde00, len=5) at sum_array.c:6
6	    int sum = 0;

结论

执行n时,程序在第16行完成calculate_sum函数调用后,直接停在main函数的第17行(不进入函数内部)。

执行s时,程序会主动进入calculate_sum函数,停在函数第6行(函数内部第一行有效代码)。

n是单步执行不进入函数,s是单步执行并主动进入函数。

8. print(缩写p)查看变量值

作用:在程序暂停时,查看指定变量的当前值,帮助验证数据是否符合预期

还是接着上面的步骤跟着我一步一步来操作:

8.1 查看已经存在的断点信息

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040061c in main at sum_array.c:16breakpoint already hit 1 time

8.2 重新启动程序

r

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/tarena/sum_array Breakpoint 1, main () at sum_array.c:16
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数

8.3 单步执行s

s

(gdb) s
calculate_sum (arr=0x7fffffffde00, len=5) at sum_array.c:6
6	    int sum = 0; //这是下一步要执行的命令,但是还没有执行
  1. 4单步执行n
(gdb) n
7	    for(int i = 0;i < len;i++){

8.5 查看sum的值

p sum

(gdb) p sum
$1 = 0

8.6 单步执行n

(gdb) n
8	        sum += arr[i];//第8行:累加元素

8.7 单步执行n

(gdb) n
7	    for(int i = 0;i < len;i++){

8.8 查看sum的值

(gdb) p sum
$2 = 10

此时你会看到sum的值从 0 变为 10,这是因为程序执行了第 8 行的sum += arr[i];语句:第一次循环中,i的值为 0,arr[0]对应数组numbers的第一个元素 10,因此sum在初始值 0 的基础上累加了 10,最终变为 10。这一变化直观反映了循环中元素累加的过程,验证了代码逻辑的正确性。

9. continue(缩写c):恢复程序执行

作用:从当前断点继续执行程序,直到遇到下一个断点或程序结束。

还是接着上面的步骤跟着我一步一步来操作:

9.1 查看已经存在的断点信息

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040061c in main at sum_array.c:16breakpoint already hit 1 time

9.2 重新启动程序

r

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/tarena/sum_array Breakpoint 1, main () at sum_array.c:16
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数

9.3 此时程序停在第16行的断点处(Breakpoint 1),若想让程序从当前位置继续执行,不再逐行调试(直到遇到下一个断点或程序结束),可使用c命令:

c

(gdb) c
Continuing.					//第一行
数组元素之和:150				//第二行
[Inferior 1 (process 4092) exited normally]  //第三行
  • 第一行:Continuing.gdb提示程序开始从当前断点继续执行。
  • 第二行:数组元素之和:150:程序执行完所有代码,输出数组{10,20,30,40,50}的累加结果(150)。
  • 第三行:[Inferior 1 (process 4092) exited normally]:表示程序正常执行完毕并退出,没有遇到其他断点。

10. quit(缩写q):退出调试

作用:退出gdb调试环境,返回终端命令行。

q

(gdb) q

11. 启用或禁用断点

当我们不想使用一个断点,也不想删除该断点时使用

11.1 disable:禁用断点

11.1.1 查看已经存在的断点信息

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040061c in main at sum_array.c:16

11.1.2 禁用这个断点

disable b 1

(gdb) disable b 1

11.1.3 查看已经存在的断点信息

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep n   0x000000000040061c in main at sum_array.c:16

你会看到Enb下方已经从y变成了n,表示禁用成功.

11.2 enable:启用断点

11.1.1 查看已经存在的断点信息

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep n   0x000000000040061c in main at sum_array.c:16

11.2.2 启用这个断点

enable b 1

(gdb) enable b 1

11.2.3 查看已经存在的断点信息

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040061c in main at sum_array.c:16

你会看到Enb下方已经从n变成了y,表示启用成功.

12. 跟踪变量与取消跟踪

12.1 display:自动跟踪变量(程序暂停时自动显示变量值)

作用:设置后,每次程序暂停(如单步执行、遇到断点),会自动显示指定变量的当前值,无需反复使用p命令,适合持续观察变量变化。

还是接着上面的步骤跟着我一步一步来操作:

info b

r

s

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000040061c in main at sum_array.c:16breakpoint already hit 1 time
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/tarena/sum_array Breakpoint 1, main () at sum_array.c:16
16	    int total = calculate_sum(numbers,length);//第16行:调用求和函数
(gdb) s
calculate_sum (arr=0x7fffffffde00, len=5) at sum_array.c:6
6	    int sum = 0;
1: sum = 0

跟踪sum变量(后续每次暂停都会自动显示其值):

(gdb) display sum
1: sum = 0
(gdb) n
7	    for(int i = 0;i < len;i++){
1: sum = 0
(gdb) n
8	        sum += arr[i];//第8行:累加元素
1: sum = 0
(gdb) n
7	    for(int i = 0;i < len;i++){
1: sum = 10
(gdb) n
8	        sum += arr[i];//第8行:累加元素
1: sum = 10
(gdb) n
7	    for(int i = 0;i < len;i++){
1: sum = 30
(gdb) n
8	        sum += arr[i];//第8行:累加元素
1: sum = 30
(gdb) n
7	    for(int i = 0;i < len;i++){
1: sum = 60
(gdb) n
8	        sum += arr[i];//第8行:累加元素
1: sum = 60
(gdb) n
7	    for(int i = 0;i < len;i++){
1: sum = 100
(gdb) n
8	        sum += arr[i];//第8行:累加元素
1: sum = 100
(gdb) n
7	    for(int i = 0;i < len;i++){
1: sum = 150

12.2 info display:查看所有跟踪的变量

作用:查看当前已设置的跟踪项(包含跟踪项的编号、跟踪项是否启用、跟踪变量或表达式),方便管理跟踪的变量。

info display

(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1:   y  sum

12.3 undisplay:取消对自动变量的跟踪

作用:移除指定的跟踪项,不再自动显示该变量的值

根据info display的结果,取消编号为1的跟踪项

undisplay 1

(gdb) undisplay 1

总结

  • display 变量名:设置自动跟踪,程序暂停时自动显示变量值(适合持续观察)。
  • info display:查看所有跟踪项的编号和变量名。
  • undisplay 编号:根据编号取消对应的跟踪项,停止自动显示。
http://www.dtcms.com/a/322268.html

相关文章:

  • 母线电压采样芯片的四大类——汽车级选型对比表
  • 101和201复制卡技术难点与解决方案
  • Express中间件和路由及响应方法
  • 软件定义车辆加速推进汽车电子技术
  • Python如何将图片转换为PDF格式
  • 2025最新高频前端面试题解析(含Vue/React/JS核心考点)
  • day30-HTTP
  • Ubuntu Server 22 虚拟机空间扩容
  • B.10.01.3-性能优化实战:从JVM到数据库的全链路优化
  • stm32项目(25)——基于stm32的植物生长箱环境监测系统
  • 微信小程序中实现表单自动填充功能的方法
  • 自动化一键部署 LNMP 环境
  • NodeJs学习日志(3):express,sequelize进行增删改查(CRUD)
  • 【QT】QMainWindow:打造专业级桌面应用的基石
  • java之父-新特性
  • 数据结构(一)顺序表
  • 【JVM】深入解析Java虚拟机
  • Ubuntu下搭建LVGL模拟器
  • react之React.cloneElement()
  • 深入剖析C++ STL原理:打开高效编程大门的钥匙
  • [每周一更]-(第155期):深入Go反射机制:架构师视角下的动态力量与工程智慧
  • Web3: DeFi借贷的安全基石, 了解喂价与清算机制的原理与重要性
  • Typora上传图片保存到assets目录下
  • ARM CPU 安全更新:Training Solo(关于 Spectre-v2 攻击中域隔离机制的局限性)
  • 学习:JS[8]本地存储+正则表达式
  • Matlab系列(004) 一 Matlab分析正态分布(高斯分布)
  • 《C++进阶之继承多态》【普通类/模板类的继承 + 父类子类的转换 + 继承的作用域 + 子类的默认成员函数】
  • pgAdmin 仪表盘的system部分不能显示,报SYSTEM_STATS扩展没有安装
  • git命令详解
  • TensorFlow深度学习实战(29)——强化学习(Reinforcement learning,RL)