一、前言
本文,我们就来讲讲如何去进行调试,对于一名优秀的程序员来说,除了要熟练写业务逻辑外,还要学会如何去调试代码,这是至关重要的!
🔰 那我们该如何去调试哪些东西呢?使用什么去调试呢?如何调试呢? —— 让我们带着上面这些问题一起进入调试的学习:book:
二、什么是Bug?
首先来讲讲我们要调试的是哪些东西,调试又称作【DeBug】,那这个Bug又是什么?
==“1949 年 9 月 9 日,我们晚上调试机器的时候,开着的窗户没有纱窗,机器闪烁的亮光几乎吸引来了世界上所有的虫子。果然机器故障了,我们发现了一只被继电器拍死的飞蛾,翅膀大约 4 英寸。”==
- 上面是计算机编程的先驱 —— 【格蕾丝·霍普】在一次的机器调试中所遇到的现象,它也是发现Bug的第一人
- 这导致计算机错误的飞蛾,也是第一个计算机程序错误
注:参考资料
三、调试是什么?有多重要?
1、导学引入
调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。
所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径:mag:
- 还记得当年我的老师曾经和我讲 “一名优秀的程序员是一名出色的侦探”,我觉得说得很正确,在日常中我们总会写很多的代码,但是呢不一定每个逻辑都能写对,这个时候就需要我们去进行调试了,==每一次调试都是尝试破案的过程==
顺着这条途径顺流而下就是犯罪( ‵▽′)ψ,逆流而上,就是真相🕵️
可是在日常的工作中很多人又是怎么写代码的呢?
下面还有《神秘的程序员们》这部漫画里的一组图片,让我们看到了一个【迷信式调试】的程序员,我们应该杜绝这种行为!
2、调试的基本步骤
好,接下去就让我们正式地来学习调试吧:computer:
首先我们要了解的是调试的基本步骤
- 发现程序错误的存在
- 以隔离、消除等方式对错误进行定位
- 确定错误产生的原因
- 提出纠正错误的解决办法
- 对程序错误予以改正,重新测试
- 我们都说【万事开头难】,想要去通过调试解决一个问题,那就需要先找到这个问题,怎么找呢?谁去找呢?这里一共会有三种人:
首先第一个的话就是程序猿本猿🐒 我们会尽量地做到让自己的代码不出问题,减少Bug
第二个的话就是软件测试人员:man: 它们的任务就是尽量多得找出程序中的Bug,保证产品在上线的时候不会出现问题
那最后一个就是在使用软件的客户🤵,因为软件做出了来就是为了给客户用的,我们会根据客户的反应来不断完善所研发出来的产品那么在找出了问题之后,接下去我们要做的就是去做一系列的排查了,这也是我们本文所要学习的内容
3、Debug和Release的介绍
接下去呢,我们再来说说有关【Debug】和【Release】的区别
🔰 Debug 通常称为调试版本
,它包含调试信息,并且不作任何优化,便于程序员调试程序
🔰 Release 称为发布版本
,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用
- 接下去我们就到VS中来观察一下
Debug版本
Release版本
- 当我们在不同的版本下去执行程序的时候,当前工程目录下就会生成对应的文件夹
- 而且对于不同的版本来说,它们所形成的可执行程序大小也各不相同,可以看到优化后的Release版本体积小了很多
int main() { char* p = "hello bit."; printf("%s\n", p); return 0; }
- 我们还可以通过汇编来进行观察,便可以发现相较于Debug版本而言,Release在底层做了许多的优化工作
四、Windows环境下VS调试介绍
好,清楚了调试的基本技巧之后,我们正式地来介绍一下在Windows中的VS下该如何去进行调试
1、调试环境的准备
- 我们在准备进行调试的时候,一定要让自己处于Debug模式下,如果是其他模式的话是无法进行调试的
2、学会快捷键
- 在调试这一块,我们一般会去使用一些快捷键来进行操作,这样在调试的过程中就可以专注于代码本身,而不是去关注进入下一步要点击哪个按键,下图中我框出来的几个就是比较常用的
F5
启动调试,经常用来直接跳到下一个断点处。
F9
创建断点和取消断点。熟知这个快捷键,我们便可以在程序的任意位置设置断点,由调试者本身来进行操控,做到得心应手。
F10
逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
F11
逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最长用的)。
Ctrl + F5
开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。
3、调试的时候查看程序当前信息
清楚该如何去进行一步步地调试之后,如果我们在调试的过程中想要查看一些当前程序的相关信息呢?该如何去进行查看
- 立马到VS中来看一下,在点击【调试】⇒ 【窗口】里面有很多的内容可供我们去进行查看,不过呢这些窗口都是需要再调试起来之后才可以的
- 我们可以看一下,如果没有调试起来点击【窗口】出现的就只会是一些简答的窗口,有些窗口我们就看不到了,所以一定要调试起来!!!
💬 接下去呢,就让我们去学习一下这些相关的窗口该如何使用:book:
3.1 查看临时变量的值
- 首先的话就是去看看我们在调试过程中所需要查看的临时变量的值,在这之前呢我们要先开启【窗口】中的【监视】,随便点开哪一个都是一样的
- 接下去,开始调试起来,我们就可以在【监视】窗口中敲入当前程序的变量名然后去观察它们在调试过程中所发生的变化,不仅可以增加也可以删除,读者可以自行去试试,这里不做演示了
- 然后我们调试起来,观察右侧的值发生了怎样的变化🎇
当然,除了【监视】窗口可以看这些内容之外,我们还可以通过【自动窗口】【局部变量】这些来进行查看
- 可以看到,我们在使用【自动窗口】进行调试的时候,发现里面的变量会随着程序的执行而发生变化,却不会是一直固定的几个值,无法进行自行输入和删除,所以我们若是像看一些变量的时候就不到了
- 再来看看【局部变量】又是怎样的
- 如果有看过 函数栈帧 的同学应该就可以知道局部变量在出了作用域之后就销毁了,所以当我们在进行调试的时候窗口中只会显示当前函数栈帧中的变量,出了当前函数的作用域之后就销毁了
综上所述,我们在进行调试的时候还是尽量选择【监视】窗口来进行观察,因为在里面我们可以任意地增、删变量,任何东西都可以由调试者来进行控制,而不是由VS来进行控制
3.2 查看内存信息
- 接下去我们再来看下一个功能 ⇒ 【内存】
- 首先你要知道的是如何精准地找到某个变量的地址,也就是在上面这个框中输入【
&
+ 变量名】即可
- 接下去我们就可以去看看当前的变量在内存中到底是怎么存放的,例如这个变量a在内存中左侧即为其地址,中间就是他的值,
0a
即为10的十六进制表示法,由于VS是小端存放,所以我们看到的样子是倒着放到的,如果不太懂得同学可以看看 数据在计算机内部的存储 一文,里面有讲到大小端的相关知识 - 那么右侧的就是一些参考信息,这一块可以不用理会
3.3 查看调用堆栈
- 如果读者看过 C/C++内存分布 的话就可以知道在内存中存在着一个区域叫做【栈区】,也可以叫做【堆栈】
- 为什么叫做堆栈呢?仔细看右侧的这个结构是不是和下面的函数布局
main()
、test1()
、test2()
、test3()
存在一些关系呢,就像是一个个地堆了上去
void test3() { printf("hello debug\n"); } void test2() { test3(); } void test1() { test2(); } int main(void) { test1(); return 0; }
- 不过,这么来看我们还看不到一些再底层的细节,此时我们就可以右击选择下面的这个【显示外部代码】,此时我们又可以看到多出来一些东西
- 可以看到,在main函数的下面我们又看到了一些类似于main函数的东西,到这里如果你看过 函数栈帧创建和销毁 的话
- 这里可以带读者通过调试来看看这个main函数的底层调用究竟是怎样的