本文通过实例操作来介绍 Visual Studio 调试器的功能。调试器在运行过程中可提供许多方法让你查看代码的情况。 你可以逐步浏览代码、查看变量中存储的值、设置对变量的监视以查看值何时改变、检查代码的执行路径、查看代码分支是否正在运行等等。本实例主要是设置断点及查看内存的操作。
DEBUG是计算机排除故障的意思。马克2号(Harvard Mark II)编制程序的格蕾丝·霍珀(Grace Hopper)是一位美国海军准将及计算机科学家,同时也是世界最早的一批程序设计师之一。有一天,她在调试设备时出现故障,拆开继电器后,发现有只飞蛾被夹扁在触点中间,从而“卡”住了机器的运行。于是,霍珀诙谐地把程序故障统称为“臭虫(BUG)”,把排除程序故障叫DEBUG,而这奇怪的“称呼”,竟成为后来计算机领域的专业行话。如DOS系统中的调试程序,程序名称就叫DEBUG。DEBUG在windows系统中也是极其重要的调试操作。
debug的意思就是清除bug,我们必须诊断出程序出了什么问题?接下来让我们来学习断点和查看内存。
一、创建一个简单的项目
1、这是main.cpp,调用log函数。
2、log.h
3、log函数的定义
二、设置断点
断点是一个标记,指示 Visual Studio 应在哪个位置挂起运行的代码,以查看变量的值或内存的行为,或确定代码的分支是否运行。 它是调试中最基本的功能。
我们首先要做的是设置一个断点,然后逐步运行程序。我们可以在程序的任何一行代码上设置断点,当执行到这一行时,它会暂停,它将悬停整个程序,我们可以查看内存情况,一个正在运行的程序,内存几乎是它的全部,所以在诊断程序时能查看内存是非常重要的。因为通过查看内存你可以看到每个变量被设置成什么样子了。
如何插入断点?以下几种方法之一:
1、方法一:
2、方法二:
快捷键F9.
3、方法三:
在下图红圈位置单击。
设置断点的位置会出现一个红圈 。
三、通过调试器运行你的代码
1、确保你处于debug模式下。
因为你在Release模式下,编译器会改变代码,你的断点有可能永远不被执行,因为你的程序被重新安排了。
2、单击本地Windows调试器
它能确保你在运行的时候附加了调制器。点击前后的变化如下图:
我们的程序将执行,并且变成这种样式。
设置断点的位置红圈上有一个黄色的箭头,指示当前指令指针所在的位置。
本地windows调试器按钮变成了继续按钮,它将像往常一样继续执行程序。
它右边还有三个重要的按钮:
这三个按钮会精确控制程序接下来会发生什么,step into逐语句F11会进入当前函数log,如果有函数,他就在这行代码中,所以这种情况下,我进入log函数。
step over逐过程F10,将转到当前函数的下一条语句。
step out跳出 shift+F11 实际上要跳出当前函数,让我们回到这个函数,在这个例子下,因为这是主函数。
F5:调试状态下运行程序
Ctrl+F5:程序运行不调试
F10:逐过程调试(遇到函数调用的地方按F10,会执行函数并跳到函数调用下一句)
F11:逐语句调试,会在函数内部执行,如果不想看函数的执行过程按Shift+F11可跳出函数执行过程。
Shift+F11:跳出函数运行(如执行到一个函数内部,或cout语句定义的地方,此时并不想看这些执行过程,则Shift+F11跳出函数)
F12:转到函数定义(把光标定位到一个函数,或变量上,按F12,会调转到函数定义或变量定义的地方
F9:断点(把光标定位到相要加断点的那一行,按F9;或者直接在那一行最前面点鼠标左键会出现一个红点)
3、step into逐语句F11来看看发生了什么
程序跳转到这里了,进入log函数的开始位置。
现在可以把鼠标悬停在message变量上并查看它,可以看出这个变量被设置成了hello world!
当我们按F10,运行到下图:
黄色箭头在这一行,意味着它还没有执行这一行。
可以看到hello world!并没有被打印出来。
当我们再按F10的时候来看看运行结果
被打印出来了。
因为我们调用了std::cout函数,它将文本打印到了控制台。
通过设置断点和逐步执行程序,我们可以逐行运行我们的程序。
四、读取内存
1、我们来添加一些变量,让程序变得更有趣一些,
运行F5
2、我们在主程序的第6行设置断点F9,F5。
我们来看一看a是什么?
为什么它是负的8.58亿?
记住:黄色箭头并不意味着我们运行了这段代码,我们正要运行它,但实际上我们还没有执行第6行,就是实际创建并设置变量的那一行。所以调试器给我们显示的是内存,因为我们还没有把变量设为任何值,它只是一个未初始化的内存,这意味着这个值只是显示给我们,并不是内存实际的值。
3、我们来看看下面这三个重要的窗口。
Autos和Locals基本上只是向你展示局部变量或者对你来说重要的变量。Watch,通常用来监控变量。可以按下变量的名字,回车。
你可以看到显示变量的值了。如果你还想查看string是什么,输入回车就可以。
4、我们有一个视图可以用来查看整个程序的内存,它的打开方式:
它打开的面板如下图:
当你想定位变量a实际存储在程序内存的位置,你只需要知道它的内存地址,要做到这一点,我们只需在Memory1窗口下的Address后面的文本框中输入输入“&”和a,变量名前面的&会取到此变量的内存地址,我们回车,就会被带到变量a的内存地址。
回车之后
这个内存是一大堆“cc”的事实,意味着它是一个未初始化的堆栈内存。 实际上编译器知道我们在尝试创建一个变量,但是我们还没有初始化,它会做一些额外的事情,比如在初始化内存之前将其设置为“cc”,很明显会使速度变慢了。我们不想在release模式中这样做,当我们真正release我们的程序或者发行游戏时,我们不想这样做,但是在调试时,它非常有用。
5、接下来,按下F10,
F10:逐过程调试(遇到函数调用的地方按F10,会执行函数并跳到函数调用下一句)
你会发现4个内存字节被设置为8.每位16进制数字是一个字节8位。
再按F10,
再再按F10,
因为是指针型的,输入指针所指地址,
104正好是十六进制的68.
68 65 6c 6c 6f,在ASCII的解释中,你可以看到它是hello。
继续按F10观察,可输入监视变量,
6、按shift+F11,可跳出当前循环。
Shift+F11:跳出函数运行(如执行到一个函数内部,或cout语句定义的地方,此时并不想看这些执行过程,则Shift+F11跳出函数)
7、但是现在只是想跳出for循环执行后面的语句,怎么办呢?
只需要重新设置断点,按F5就可以。