VS的调试功能小结

巧用VS的调试功能可以帮助我们更好的理解程序,追踪变量各种值的变化等等,对循环、函数和递归的学习也有帮助。

如果你已经掌握了VS调试技巧,本文可以跳过(去年的稿qaq)。

以下内容主要摘自网络。

写在前面[1]

初学 C 语言程序设计,往往一看到自己编的程序出现错误就不知所措了。有些同学上机时,只要程序能够顺利运行,就认为大功告成,根本没想到程序还存在某些隐患。要想不犯或少犯错误,就需要了解 C 语言程序设计的错误类型和纠正方法。 C 语言程序设计的错误可分为语法错误、连接错误、逻辑错误和运行错误。

语法错误:在编写程序时违反了 C 语言的语法规定。语法不正确、关键词拼错、标点漏写、数据运算类型不匹配、括号不配对等都属于语法错误,在进入程序编译阶段,编译系统会给出出错行和相应“出错信息”。我们可以双击错误提示行,将光标快速定位到出错代码所在的出错行上。根据错误提示修改源程序,排除错误。

连接错误:如果使用了错误的函数调用,比如书写了错误的函数名或不存在的函数名,编译系统在对其进行连接时便会发现这一错误。纠正方法同 1。

逻辑错误:虽然程序不存在上述两种错误,但程序运行结果就是与预期效果不符。 逻辑错误往往是因为程序采用的算法有问题,或编写的程序逻辑与算法不完全吻合。逻辑错误比语法错误更难排除,需要程序员对程序逐步调试,检测循环、分支调用是否正确,变量值是否按照预期产生变化。

运行错误:程序不存在上述错误,但运行结果时对时错。 运行错误往往是由于程序的容错性不高,可能在设计时仅考虑了一部分数据的情况,对于其他数据就不能适用了。例如打开文件时没有检测打开是否成功就开始对文件进行读写,结果程序运行时,如果文件能够顺利打开,程序运行正确,反之则程序运行出错。要避免这种类型的错误,需要对程序反复测试,完备算法,使程序能够适应各种情况的数据。

如果语法和连接错误,程序直接就跑不起来;如果逻辑错误,建议梳理思路,想想那里写错了;运行错误也不怕,多踩几次坑多积累经验,比如是不是0做了除数?是不是爆了栈?是不是数组下标越了界?指针不可用?……

L学长上周提到的“在程序里声明数组的位置”问题,就是一个很好的小tip。相信大家都能体会到数组范围大一点的时候,全局于int main里面的局部的差异了吧。

VS篇

基本操作[5]

F9/单击设置断点->F5(开始调试(运行至断点处))->F11(逐语句(遇到函数则进入函数))

shift+F5 结束调试

shift+F11 退出函数

悬停鼠标查看表达式

这个最简单了大家应该都会……

如果你把你鼠标指向你感兴趣的一个变量,你会发现事情简单多了。而且,类和结构体可以通过单击展开。这样你就可以方便快捷的找到你想查看的变量了。

https://pic002.cnblogs.com/images/2012/281842/2012100310453069.png

调试有时候很有挑战性,当你步入一个函数想看看哪块出错的时候,查看调用栈来想想值是从哪来的。另一些情况下,则需要添加一些监视表达式,或者查看局部变量列表,这通常还是花费一些时间的,但是。

设置下一条语句

https://pic002.cnblogs.com/images/2012/281842/2012100310455469.png

一个典型的调试情况就是通过单步跟踪分析为什么一个函数调用失败了。当你发现一个函数调用的另一个函数返回错误的时候你会怎么做?重启调试?有更好的方法。拖动这个黄色的语句标识到你想下一步执行的语句前就可以了。比如你刚才失败的那块,然后步入。

编辑然后继续

了解即可。

https://pic002.cnblogs.com/images/2012/281842/2012100310460339.png

调试一个复杂的程序的时候,在一个被调用很多次的函数处发现一个错误,但又不想浪费时间停下来、重新编译然后重新调试。没问题,可以仅仅在该处改正代码然后继续单步,VS会修正程序,然后继续调试,不需要重启。

注意,“编辑然后继续”有一些已知限制,比如改变在一个方法里应该是局部的。如果你改变了方法签名,添加一些新方法或是类,那就不得不重启程序或者撤销改变来继续了。改变方法也包含lambda表达式隐式修改的自动生成的代理类,因此也不能继续。

方便的监视窗口

https://pic002.cnblogs.com/images/2012/281842/2012100310461322.png

大概现代的调试器都有一个监视窗口。

VS允许你简单的添加或移除变量。单击空行,输入你的表达式按下回车,或者是在不需要的表达式上按下Delete键就可以删除了。

从监视窗口你不仅仅可以看到“正常”的变量:你可以输入$handles 来追踪你的程序打开了多少句柄(可以方便的修复内存泄漏),输入$err 可以看到上一个函数的错误码,然后使用工具-错误信息可以看到更详细的描述,或者输入@eax(64位是@rax)来查看包含函数返回值的寄存器。

条件断点

https://pic002.cnblogs.com/images/2012/281842/2012100310464974.png

如果你尝试通过断点再现一个罕见的事件,该情况引发了一些严重的错误,那么,你可以添加条件断点。

定义一个断点的条件,然后如果条件不成立,VS会忽略该断点。

详细可参看后面的示例。

内存窗口 

https://pic002.cnblogs.com/images/2012/281842/2012100310470168.png

有些bug由不正确的结构体定义引起,忽略的对齐属性等等。查看内存中的内容可以定位然后修复bug。

VS提供了一个内存窗口,可以把值以8/16/32/64位的形式展示。还有浮点值。也允许实时改变它们,就像文本编辑器。

注:原资料更有列出十一种之多,感兴趣的同学可以去看看英文版网页[8]和翻译版博客[7]。

示例一[4]

打断点

在侧栏点击一下,即可生成断点,如图1-1红点所示。

功能: 在调试时可以运行到这一步之后停止,后可配合单步调试进行调试

https://img-blog.csdnimg.cn/20200224163415209.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNDQ0OTQ3,size_16,color_FFFFFF,t_70

图1-1 打断点

调试启动,如图1-2:

https://img-blog.csdnimg.cn/20200730104821442.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNDQ0OTQ3,size_16,color_FFFFFF,t_70

图1-2 调试启动

效果,如图1-3:

https://img-blog.csdnimg.cn/20200224163554580.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNDQ0OTQ3,size_16,color_FFFFFF,t_70

图1-3 效果图

单步调试

单步调试快捷键F11,可以逐步运行,也可以在断点调试下接单步,进而可以仔细检查每一步是否符合预期, 从而能够检查每一步是否符合预期,比如for循环是否越界,赋值是否正确等。

F10是逐过程调试,不会进入函数块。

示例二[6]

1、单步调试(F10)

在Debug状态下,按F10,逐行调试。

2、逐句调试(F11)

在Debug状态下,按F11,逐句调试,遇到函数会进入函数内部调试。

3、跳到当前光标处(Ctrl+F10)

先把光标定位在目标代码位置,然后Ctrl+F10,程序会直接跳到该行代码位置停下。

4、条件中断

在某些场景下,比如循环中,程序员需要知道循环的情况,但是因为循环次数过大,逐句调试显然不是明智的选择,这时可以选择“条件中断”。即满足特定预设条件后作出的中断响应。

首先在断点位置处右键,依次选择断点、条件。

https://img-blog.csdnimg.cn/20190612162028370.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3Nzg3OTkz,size_16,color_FFFFFF,t_70

图2-4条件中断

这时在弹出的对话框中设置你需要的中断条件,比如我们希望变量 i 大于10的时候就中断:图2-5。

https://img-blog.csdnimg.cn/20190612162113429.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3Nzg3OTkz,size_16,color_FFFFFF,t_70

图2-5 设置中断条件1

或者希望某一变量到达某一值时就触发断点,比如我们希望变量 i ==90的时候就停下:图2-6。

https://img-blog.csdnimg.cn/20190612163201542.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3Nzg3OTkz,size_16,color_FFFFFF,t_70

图2-6 设置达到变量值的中断结束条件2

VC篇

为了方便程序员排除程序中的逻辑错误,VC 提供了强大的调试功能。每当我们创建一个新的 VC 工程项目时,默认状态就是 Debug(调试)版本。调试版本会执行编译命令_D_DEBUG,将头文件的调试语句 ifdef 分支代码添加到可执行文件中;同时加入的调试信息可以让开发人员观察变量,单步执行程序。由于调试版本包含了大量信息,所以生成的 Debug 版本可执行文件容量会远远大于Release(发行)版本。

设置断点

VC 可以在程序中设置断点,跟踪程序实际执行流程。设置断点后,可以按“F5”功能键启动 Debug 模式,程序会在断点处停止。我们可以接着单步执行程序,观察各变量的值如何变化,确认程序是否按照设想的方式运行。 设置断点的方法是:将光标停在要被暂停的那一行,选择“Build MiniBar”工具栏按钮“Insert/Remove Breakpoint (F9)”按钮添加断点,如图2-1所示,断点所在代码行的最左边出现了一个深红色的实心圆点,这表示断点设置成功。

https://images0.cnblogs.com/i/195755/201404/051337234211277.jpg

图2-1. VC断点设置成功

如果该行已经设置了断点,那么再次按“F9”功能键会清除该断点。

调试命令

我们也可以在 VC“Build”(组建)菜单下的“Start Debug”(开始调试)中点击 Go(F5)命令进入调试状态,Build 菜单自动变成 Debug 菜单,提供以下专用的调试命令:

Go(F5) 从当前语句开始运行程序,直到程序结束或断点处。

Step Into(F11) 单步执行下条语句,并跟踪遇到的函数。

Step Over(F10) 单步执行(跳过所调用的函数)。

Run to Cursor(Ctrl+F10) 运行程序到光标所在的代码行。

Step out(Shift+F11) 执行函数调用外的语句,并终止在函数调用语句处。

Stop Debugging(Shift+F5) 停止调试,返回正常的编辑状态。

必须在运行程序时用 Go 命令(而不是 Execute)才能启动调试模式。在调试模式下,程序停止在某条语句,该条语句左边就会出现一个黄色的小箭头。我们随时中断程序、单步执行、查看变量、检查调用情况。比如,按“F5”功能键进入调试模式,程序运行到断点处暂停;不断按“F10”功能键,接着一行一行地执行程序,直到程序运行结束。

需要说明的是,如果希望能一句一句地单步调试程序,在编写程序时就必须一行只写一条语句。

查看变量

单步调试程序的过程中,我们可以在下方的Variables (变量)子窗口和Watch(监视) 子窗口中动态地察看变量的值,如图2-2所示。Variables 子窗口中自动显示当前运行上下文中的各个变量的值变量,而 Watch 子窗口内只显示在此 Watch 子窗口输入的变量或表达式的值。随着程序的逐步运行,也可以直接用鼠标指向程序中变量查看其值。例如在图 2-2中,我们可以清楚地看到,程序已经为自动型变量 first、second、big 分配了内存,但它们的初始值是随机的。

注:不建议在写程序的时候起first、second这样的变量名,与c的保留字和标识符重名。

https://images0.cnblogs.com/i/195755/201404/051337321569041.jpg

图2-2. VC查看变量

Variables 子窗口有 3 个选项卡:Auto、Locals 和 This。

Auto 选项卡:显示出当前语句和上一条语句使用的变量,它还显示使用 Step over 或 Step out 命令后函数的返回值。

Locals 选项卡:显示出当前函数使用的局部变量。

This 选项卡:显示出由 This 所指向的对象(C语言用不到this)。

如果变量较多,自动显示的Variables 窗口难以查看时,还可以在右边的Watch 子窗口中添加想要监控的变量名。例如,图2-2在 Watch1 子窗口中添加了变量“first”。我们还可以直接将变量拖动到 Watch 子窗口的空白 Name 框中。 添加结束后,该变量的值会被显示出来。并且随着单步调试的进行,会看到变量 first 的值逐渐变化。如果各变量的值按照设想的方式逐渐变化,程序运行结果无误,本次开发就顺利结束了。如果发现各变量值的变化和设想的不一致,说明程序存在逻辑错误,那就需要停止调试,返回编辑窗口,查错并修改程序。

查看内存

数组和指针指向了一段连续的内存中的若干个数据。可以使用 memory 功能显示数组和指针指向的连续内存中的内容。在 Debug 工具条上点 memory 按钮,弹出一个对话框,在其中输入数组或指针的地址,就可以显示该地址指向的内存的内容。如图2-3所示:

https://images0.cnblogs.com/i/195755/201404/051337395318021.jpg

图2-3. VC查看内存

常用的默认快捷键小结[2]

调试快捷键

F5:        开始调试

Shift+F5:    停止调试

F10:        调试到下一句,不进入函数内部

F11:        调试到下一句,跟进到有代码的函数内部

Shift+F11:从当前函数中跳出

Ctrl+F10:    调试到光标所在位置

F9:        设置(取消)断点

Alt+F9:    高级断点设置

跟踪调试

1.尽量使用快捷键进行调试。

2.观察调试信息。

3.高级中断设置。

异常调试

重试->取消->调试。

函数堆栈,用variables或者call stack窗口。

Release调试

1.经常测试你的Debug和Release版本。

2.不要移除调试代码,如ASSERT, TRACE等。

3.初始化变量,特别是全局变量,malloc的内存,new的内存。

4.当你移除某个资源是,确保你移除了所有跟这个资源相关的申明(主要是在resouce.h文件中)。

5.使用3或者4级的警告级编译你的代码,并确保没有警告,project->setting->c/c++ ->warning level(中文版是项目-〉属性-〉C/C++-〉常规-〉警告等级)。

6._debug改成NDEBUG进行调试,project->setting->c/c++ ->Preprocessor definitions(中文版是项目-〉属性-〉C/C++-〉预处理器-〉预处理器定义)(这里是debug和Release编译的重要不同之一)。

7.在Release中调试源代码,project->setting->c/c++ -> debug info选择programDatabase(中文版是项目-〉属性-〉C/C++-〉常规-〉调试信息格式-〉用于“编辑并继续”的程序数据库),project->setting->link 选上Generate debug info(中文版是项目-〉属性-〉链接器-〉调试-〉生成调试信息)。

8.走读代码,特别关注堆栈和指针。

写在最后:

随时输出变量值(printf\puts…)也是可以的,只要对改代码和理解算法流程有帮助……相信大家都会积累经验,总结出得心应手的好tips。

写程序还是要细心呀。

祝大家AC愉快。

参考资料:

1.VC调试相关

VC++6.0环境下调试c语言代码的方法和步骤_附图_weixin_34383618的博客-CSDN博客

2.VC调试方法

VC调试方法_坚持,才是力量.老实做人,踏实做事.-CSDN博客

3.VS2019的调试实例(烫烫烫)

VS2019的调试功能学习(烫烫烫)_码上有的博客-CSDN博客_vs2019调试

4.VS调试相关:

调试——vs单步调试及断点调试基本介绍(面向小萌新)_——深情——的博客-CSDN博客_vs2019怎么单步调试

5.VS调试基本操作

VS单步调试_Legends_Never_Die的博客-CSDN博客_vs单步调试

6.VS调试相关:

VS2015调试基础_m0_37787993的博客-CSDN博客_vs2015调试

7. [原译]11个高效的VS调试技巧

 [原译]11个高效的VS调试技巧 - lazycoding - 博客园

8. 10+ Powerful Debugging Tricks with Visual Studio

 10+ Powerful Debugging Tricks with Visual Studio - CodeProject


版权声明:本文为ACM2017原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。