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

 

文件描述符

目录

内存管理

前言

AHB5

正文

生命周期

一、打断点

建模国赛

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

Mysql笔记

三、逐过程执行

uniapp小程序微信支付

三、运行到光标处

原神模型

四、多次执行代码

位置闭环控制

五、快速监视

3d渲染

六、监视窗口

C语言必背100代码

八、内存查看

OneDNS

九、局部变量

hive

十、调用堆栈

无法解析

十一、assert的使用

游戏框架

十二、条件断点

rsync

十三、函数断点

repo


MySQL主从同步

前言

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

练习题

正文

一、打断点

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

mysql优化

dump文件

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

并发

无线收发模块

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

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

hibernate

工厂方法模式

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

例如,上面第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函数的位置: 

 

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注