【C++】Visual Studio调试C++代码的13个技巧

 

目录

前言

正文

一、打断点

二、逐语句执行和跳出执行

三、逐过程执行

三、运行到光标处

四、多次执行代码

五、快速监视

六、监视窗口

八、内存查看

九、局部变量

十、调用堆栈

十一、assert的使用

十二、条件断点

十三、函数断点


前言

  • 本文使用的是Visual Studio 2022社区版,但在老版本上依然适用(例如2019版)。
  • 本文旨在简单介绍一些调试的小技巧,进阶的调试技巧以后再做总结。
  • 本文基于Windows平台

正文

一、打断点

启用调试,第一步需要打断点:

注意:启动调试后,程序会执行到第一个断点出暂停,这里的第一个断点指的不是位置上的第一个(即代码行最靠前的那一个),而是逻辑上的第一个。例如上图,第24行的断点是最先执行的那一个。

二、逐语句执行和跳出执行

逐语句执行也叫“单步执行”或“逐行执行”,如果调用了一个函数,那么会进入这个函数中,而不是跳过函数。 

注意:这里的“逐行”,不是物理意义上的每一行,而是逻辑上的一行代码

例如,上面第24行,判断条件里调用了2个函数,那么,会首先进入add()函数,返回后再进入sub()函数。

        注意:C++编译器对条件判断有一个优化,对于24行,如果add()为false,那就不会继续执行sub()了,如果add()为true,才会继续进入sub()函数。

跳出执行的用处是,如果进入函数后,不再希望用单步执行走完函数体内剩余的代码,那么可以跳出执行,直接返回:

 

三、逐过程执行

特点:无论当前代码行有多少个函数调用,都不会进入到函数中,而是直接进入到下一行代码并暂停。

三、运行到光标处

在没启动调试的时候,直接在想要定位的代码行处右键,选择运行到光标处,那么就会自动设置一个一次性断点,开始调试:

 

 注意:一次性断点的优先级是低于其它断点的,如果调试之前在某个位置打了断点A,并且这个断点在一次性断点之前(逻辑上),那么启用调试后,会首先来到断点A处。

四、多次执行代码

如果有些地方没弄清楚,那么可以在不重新打开调试的情况下,多次执行某些代码。

例如:

断点打在10行,执行完10行后,来到11行,但如果我还想再执行一次第10行,那就是“多次执行代码”了,方法很简单,就是:用鼠标把那个黄色箭头拖到第10行:

连续执行2次第10行: 

 

 

五、快速监视

在调试过程中,如果想要快速查看某个对象的信息,那么快速监视就挺有用,同时,还可以修改这个对象的值。

方法是:选中某个变量,右键:

 

 

六、监视窗口

调试的时候,如果要查看的变量很多,就需要用监视窗口,同时,可以打开多个监视窗口:

注1:和快速监视一样,监视窗口也能修改变量的值。只不过对于string这类较为复杂的类型来说,修改就相对麻烦,不能一次性修改,而是找到每个字符对应的位置,再修改:

注2:也可以监视一些表达式(有些不行,例如构造、析构、类型转换、预处理器宏等):

 

八、内存查看

目前还没有从内存层面去找bug,所以就举个查看内存的例子。

首先,启用调试,然后打开内存窗口:

默认窗口没有什么内容,只有一些随机的值:

当需要查看某个变量的内存占用时,只需要把这个变量拖到内存窗口:

同理,把wa也拖上去,对比a和wa的内存占用情况,可以看到wa是宽字符,每个字符占2个字节:(最后有一个结束符,也需要占一个字符大小的内存空间)

 

 

九、局部变量

  • 用来查看当前作用域下的变量:

test()里的p:  

 

 test0()里的p:  

 

 

  •  变量太多时,可以筛选想要显示的变量:

 

 

 

十、调用堆栈

可以查看函数的调用情况,每一个函数调用叫做“帧”,也称为“栈帧”;

栈底的函数最先被调用,栈顶的函数最后被调用:

 

十一、assert的使用

首先看一个很熟悉的窗口:

 这个实际上就是通过assert产生的,如果出现,不要点“中止”,而是点“重试”,这样就能找到代码出错的地方:

注意:

  1. 要导入头文件 #include <assert.h>
  2.  不要在assert里使用函数调用、对变量赋值!

关于第2点,《C++ Primer》第216页讲到,如果源文件定义了NDEBUG宏,那么assert就会失效,从而assert里的函数调用和赋值等操作都会被忽略,导致后面的错误!下面举例:

如果没有定义NDEBUG宏,那么assert是生效的,因此add函数会把相加的结果赋值给c:

#include <assert.h>
int add(int a, int b)
{
	return a + b;
}
void test()
{
	int a = 10, b = 10;
	int c = 0;
	assert((c = add(a, b)) == 20); // 调用函数并赋值

	cout << c; // 输出20
}

如果定义NDEBUG宏:

#define NDEBUG
#include <assert.h>
int add(int a, int b)
{
	return a + b;
}
void test()
{
	int a = 10, b = 10;
	int c = 0;
	assert((c = add(a, b)) == 20); // 这条语句会被忽略

	cout << c; // 输出0
}

 

十二、条件断点

通过举例来讲解:假如有一个for循环,需要在到达某个条件的时候,调试才停下来,而不是从一开始就停下来,那么此时就可以用条件断点:

 

启动调试: 

 

 

十三、函数断点

当我们知道一个函数名,但不知道函数具体在哪里的时候;或者函数被重载的时候,希望在每次调用函数的时候能够暂停;此时需要用到函数断点:

 

启动调试,就会自动来到add函数的位置: 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值