1.什么是程序的调试?
程序调试的目的就是找出程序中隐藏的故障,校正那些不正常的指令,使程序能够正常工作。
2.调试的类别
程序的调试有几种不同的级别。最高级别当然是编程人员通过自己肉眼观察和推断,找出那些有毛病的代码并修改之。最低级别是对汇编代码进行调试。由于汇编语言代码的复杂、冗长与不直观。在汇编级对程序进行调试是一件比较费力的事。
使用得最多的大概还是源程序级即符号级的调试,在此种调试方式下我们能够把高级语言的语句当作一个可执行的最小单位,在调试程序中使用源程序中出现的符号和代码的行号来交互式地引用变量或控制语句的执行。
3.调试的内容
首先,要了解调试一个程序的哪方面内容,再使用调试器提供的工具,进行相关调试。
比如,数据的测试,语法错误,程序逻辑错误。
4.调试过程
(1)启动调试器:启动gdb调试器,gdb filename //filename是可执行文件
(2)列出源程序:list命令,一次显示10行。
如:
- (gdb) list //一次显示十行程序
- (gdb) list <line-number> //显示参数行之前和之后的10行内容,比如list 10,会将5-14之间的内容打印出来
- (gdb) list <line1,line2> //显示参数之间的内容
(3)运行程序:run <arg1 arg2...>
arg1,2是传递给程序的参数。
可以使用set命令,设置传递给程序的参数,然后直接run即可
- set args argument1 argument2
(4) 设置断点
- 1.设置断点
- (gdb)break <line-number> //某行设置
- (gdb)break <function-name> //某个函数前设置断点
- 2.有时需要查看运行中变量等于某个值时,程序的状态,比如for循环执行到第几次时相关变量的值。这时可以使用断点。
- (gdb)break <line-number> if <conditional expression> //表达式符合C语法
- (gdb)break 10 if i==3
- 注:条件表达式中的变量必须是在设置断点的行的变量作用域内。
- 3.如果断点已经设置好,可以使用condition 命令为指定断点添加条件
- (gdb)condition break-number if <expression>
- 4.可以在指定的源文件中设置断点
- (gdb)break <filename:line-number>
- (gdb)break <filename:function-name>
- 如:(gdb)break main.c:10
- (gdb)break main.c:main
- 5.删除断点
- (gdb)info break //打印所有断点信息
- (gdb)delete breakpoint <point-number>
- 6.禁止或使能断点
- (gdb)break <line-number>
- 7.清除断点
- (gdb)clear
- (gdb)clear <number>
-
- 8.观察点(数据断点)
- (gdb)watch <condition>
- 断点与观察点的区别:1.所有使用与breakpoint的操作都适用于watchpoint
- 2.断点是CPU到某一地址取指令时中断,观察点是CPU到某一地址读写数据时中断。
-
(5)查看运行时数据
在调试中,需要了解各变量的value,程序的运行状态。
- 1.数据观察命令
- (gdb)print <variable-name>
- 在GDB中,可以查看全局变量、静态局部变量、当前作用域有效的局部变量。如果局部变量与全局变量冲突,而需要查看全局变量的值,可以使用“::”操作符
- (gdb)print <file::variable>
- (gdb)print <function::variable>
- 如:
- (gdb)print ‘main.c‘::a
- 2.对程序中函数的调用
- 调用函数时指定形参的值
- (gdb)print func(arg1,arg2,...)
- 3.查看表达式的值
- (gdb)print <expression>
- 4.查看数组的值
- (gdb)print <array-name>
- gdb中@操作符的使用:
- (gdb)print *array@len
- array是一段连续的内存空间的起始地址,len是需要查看内存的长度。
- array可以是数组,也可以是动态分配的堆区间。
- 如果是静态数组,直接:
- (gdb)print 数组名
- 就能显示全部数据
- 5.查看内存
- examine命令,可以让用户查看指定内存中的值
- (gdb)examine /nfu <address>
- 参数:
- n:一个正整数,表示显示几个单位的内存
- f:显示的格式,进制(u十六进制)或字符串(s)或指令(i)
- u:单位,b=1 字节, h=2 字节,w=4字节,g=8 字节。
- 6.查看寄存器
- (gdb)info registers
- (gdb)info all-registers
- (gdb)info registers-name
-
- 7.自动显示变量
- 使用display命令,可以在程序单步执行或遇到断点时,自动显示变量值。
- (gdb) display/fmt expr
- 参数:fmt,可以是 十六进制(x),十六无符(u)
- 十进制(d) 八(o) 二进制(t) 字符格式(c) 浮点格式(f)
-
- info display可以查看display设置的自动显示的信息。
- undisplay删除自动显示。disable与enable也适用于该命令。
5.使用GDB跟踪栈上数据(
栈中数据都是局部变量)
每个函数和它的变量都有一个栈帧-frame,当前栈帧总是frame 0。所有这些frame形成一个程序完整的运行栈。可以使用backtrace命令打印栈帧的信息。
命令:
(gdb)bt
如果在main函数里调用了func()函数,此时正在执行func()函数,这时的栈帧frame 0 就是func()函数的。
使用命令info locals 打印的变量就是当前栈帧的。
如果要查看main()函数的栈帧中的局部变量,可以使用命令"(gdb) frame 1 "进行切换。