C语言学习第十一天-调试教程

调试的概念

在编写代码的过程中,相信大家肯定遇到过这样的情况:代码能够编译通过,没有语法错误,但是运行结果却不对,反复检查了很多遍,依然不知道哪里出了问题。这个时候,就需要调试程序了。

所谓调试(Debug),就是让代码一步一步慢慢执行,跟踪程序的运行过程。比如,可以让程序停在某个地方,查看当前所有变量的值,或者内存中的数据;也可以让程序一次只执行一条或者几条语句,看看程序到底执行了哪些代码。

在调试的过程中,我们可以监控程序的每一个细节,包括变量的值、函数的调用过程、内存中的数据、线程的调度等,从而发现隐藏的错误或者低效的代码。

编译器可以发现程序的语法错误,调试可以发现程序的逻辑错误。所谓逻辑错误,是指代码思路或者设计上的缺陷。

对于初学者来说,学习调试也可以增加编程的功力,它能让我们更加了解自己的程序,比如变量是什么时候赋值的、内存是什么时候分配的,从而弥补学习的纰漏。

调试器

调试需要借助专业的辅助软件-调试器。主流的C/C++调试器有下面几种:

Remote Debugger、WinDbg、LLDB、GDB。

设置断点,开始调试

默认情况下,程序不会进入调试模式,代码会瞬间从开头执行到末尾。要想观察程序的内部细节,就得让程序在某个地方停下来,我们可以在这个地方设置断点。

所谓断点(BreakPoint),可以理解为障碍物,人遇到障碍物不能行走,程序遇到断点就暂停执行。

调用堆栈可以看到当前函数的调用关系。

断点窗口可以看到当前设置的所有断点。

即时窗口可以让我们临时运行一段代码。

输出窗口用来显示程序的运行过程,给出错误信息和警告信息。

自动窗口会显示当前代码行和上一行码行中所使用到的变量。

局部变量窗口会显示当前函数中的所有局部变量。

线程和模块窗口暂时无需理会。

断点的真正含义

严格来说,调试器遇到断点时会把程序暂时挂起,让程序进入一种特殊的状态-中断状态,这种状态下操作系统不会终止程序的执行,也不会清除与程序相关的元素,比如变量、函数等,它们在内存中的位置不会发生变化。

关键是,处于中断状态下的程序允许用户查看和修改它的运行状态,比如查看和修改变量的值、查看和修改内存中的数据、查看函数调用关系等,这就是调试的奥秘。

继续执行程序

点击“运行”按钮或者按F5键即可跳过断点,让程序恢复正常状态,继续执行后面的代码,直到程序结束或者遇到下一个断点。

在调试过程中,按照上面的方法可以设置多个断点,程序在执行过程中每次遇到断点都会暂停。

删除断点

如果不希望程序暂停,可以删除断点。删除断点也很简单,在原有断点处再次单击鼠标即可,也可以将光标定位到要删除断点的代码行,再次按F9键,或者在右键菜单中删除。

代替暂停语句

在VS下,程序运行结束后不会自动暂停(一闪而退),要手动添加暂停语句system("pause"); ,如果大家觉得麻烦,也可以在代码最后插入断点,强制程序暂停。

查看和修改变量的值

设置了断点,就可以观察程序的运行情况了,其中很重要的一点就是查看相关变量的值,这足以发现大部分逻辑错误。

添加监视

如果你希望长时间观测某个变量,还可以将该变量添加到监视窗口。

添加监视之后,每次变量的值被改变都会反映到该窗口中,无需再将鼠标移动到变量上方查看其值。尤其是当程序稍大时,往往需要同时观测多个变量的值,添加监视的方式就会显得非常方便。

单步调试(逐语句调试和逐过程调试)

单步调试,就是让代码一步一步地执行。

逐过程调试(F10)和逐语句调试(F11)都可以用来进行单步调试,但是它们有所区别:

逐过程调试(F10)在遇到函数时,会把函数从整体上看做一条语句,不会进入函数内部;

逐语句调试(F11)在遇到函数时,认为函数由多条语句构成,会进入函数内部。

修改代码运行位置

调试器还允许我们直接跳过一段代码,不去执行它们。

随意修改程序运行位置是非常危险的行为,假设我们定义了一个指针,在第N行代码中让它指向了一个数组,如果我们在修改程序运行位置的时候跳过了第N行代码,并且后面也使用到了该指针,那么就极有可能导致程序崩溃。

即时窗口的使用

“即时窗口”是VS提供的一项非常强大的功能,在调试模式下,我们可以在即时窗口中输入C语言代码并立即运行。

在即时窗口中可以使用代码中的变量,可以输出变量或表达式的值(无需使用printf()函数),也可以修改变量的值。

即时窗口本质上是一个命令解释器,它负责解释我们输入的代码,再由VS中的对应模块执行,最后将输出结果呈现到即时窗口。

需要注意的是,在即时窗口中不能定义新的变量,因为程序运行时 Windows 已经为它分配好了只够刚好使用的内存,定义变量是需要额外分配内存的,所以调试器不允许在程序运行的过程中定义变量,因为这可能会导致不可预知的后果。

调用函数

在即时窗口中除了可以使用代码中的变量,也可以调用代码中的函数。

查看、修改运行时的内存

类型名变量类型内存查看窗口中应选择的数据格式
short16位整型2字节整数
int32位整型4字节整数
long32位整型4字节整数
long long64位整型8字节整数
float32位单精度浮点数32位浮点
double64位双精度浮点数64位浮点

assert断言函数

assert 函数的用法很简单,我们只要传入一个表达式即可,它会计算我们传入的表达式的结果,如果为真,则不会有任何操作,但是如果我们传入的表达式的计算结果为假,它就会像 stderr (标准错误输出)打印一条错误信息,然后调用 abort 函数直接终止程序的运行。

错误,是代码编写途中的缺陷导致的,是程序运行中可以用 if 语句检测出来的。而异常在我们的 C 语言中,一般也可以使用 if 语句判断,并进行对应的处理。而 assert 是用来判断我们程序中的代码级别的错误的。像用户输入错误,调用其他函数库错误,这些问题我们都是可以用 if 语句检测处理的。另一方面,使用 if 语句的程序对用户更友好。

在发布版程序中,所有的 assert 语句都会失效,所以不要使用会改变环境的语句作为断言函数的参数,这可能导致实际运行中出现问题。

调试信息的输出

我们可以用一个 Windows 操作系统提供的函数-OutputDebugString,在调试器的调试窗口上输出我们自己的调试信息。这个函数非常常用,它可以向调试输出窗口输出信息(无需设置断点,执行就会输出调试信息),并且一般只在绑定了调试器的情况下才会生效,否则会被 Windows 直接忽略。

首先,这个函数在 windows.h 中被定义,所以我们需要包含 windows.h 这个头文件才能使用 OutputDebugString 函数。这个函数的使用方式非常的简单,它只有简单的一个参数——我们要输出的调试信息。但是有一点值得注意:准确来说 OutputDebugString 并不是一个函数,他是一个宏。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值