学了编程却写出错误代码?程序运行结果与想象不符?当bug出现时该何去何从,别担心,这篇文章统统告诉你!手把手带你调试代码,让bug原形毕露!

这时候应该怎么办呢/p>

别急,现在立即教你使用调试技巧。

手把手带你搞定bug。

什么是bug

大家看看下面这张图:

我们可能每天都会碰到各种各样的问题,但是是不是遇到了问题就像上图一样,感觉可能是这里出了问题,就改改这里试试看,又或者改改那里,实际上到底是为什么却不知道呢/p>

这种调试的方法被形象地称为迷信式调试,我们只是跟着感觉走,但是却不明就里。所以,我们要拒绝迷信式的调试,从先在开始,学会科学的调试方法。

而且,最重要的是,我们要在调试的过程中充分思考,把思维转起来,所以,在往下阅读文章之前,一定一定一定要先动起来给博主点个赞。

Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。

这是因为Release 版本进行了各种优化,使程序在代码大小和运行速度上都是最优的。但也因此,Release版本是不能进行调试的。

在大部分情况下,Release版本中的exe文件是可以直接发给其他人使用的,比如我们在写关机程序的时候,就直接把Release版本的关机程序发给了我们的好朋友。(详见:【一张图搞定关机程序】让你的代码有趣起来!送兄弟送闺蜜,快乐原来如此简单!(赋全过程和结果,超详细解说))

以上是Debug和Release版本的一个简单区别,但是Release版本除了在大小上会进行优化,还会对代码进行优化。

下面我们分别在Debug版本和Release版本中对代码进行调试。

会发现Debug版本可以一步一步地进行调试,在屏幕上依次打印出arr数组的元素。

我们知道Debug是调试版本,Release是发布版本,那么这里有一个问题:

测试人员测试的是哪个版本呢/p>

答案应该是发布版本。因为测试人员应该是站在用户的角度对软件进行测试的,所以应该用发布版本。并且调试版本在测试完之后转为发布版本时也可能因为优化而导致问题,所以测试人员要测试的是发布版本。

windows环境调试介绍

那我们在Windows环境下是怎么调试的呢/p>

调试环境的准备

首先我们得准备好我们的调试环境。

即把程序改成Debug版本。

F5:启动调试。经常和F9配合使用,用来直接调到下一个断点处,如果没有设置断点,则程序执行到底。

F9:创建断点和取消断点。

这时候再按住F5调试代码。

监视窗口

比如监视窗口:我们可以通过监视窗口观察任何我们想观察的变量。比如arr[4]、i+8、或者&arr等等,只要我们输进去即可。

局部变量

调用堆栈

即通过查看C语言的汇编信息。

寄存器

那么接下来我们就按F10进入调试。

打开监视窗口,输入我们要监视的值:n、ret、sum、i

确定了问题所在:ret在每次求完阶乘之后没有重置,所以我们应该把ret进行重置。

有一下两种解决方案:

从以上的例子中,我们可以看到,当程序出了问题的时候,我们要对代码进行调试,定位出问题出现的大致位置。

然后推测问题出现的原因,初步确定问题可能的原因,然后再着手调试代码确定问题所在。

我们应该对自己写的代码做到心里有数,只有这样才能在调试的时候确定代码是不是按照我们的思路走的,然后才能找出问题的原因。

实例二

接下来我们再来看一个例子。

我们知道这段代码是有问题的,数组只是越界了,为什么程序会造成死循环呢/p>

我们可以通过调试的方法找一下原因。

而我们之前在操作符讲解中就提过,arr是数组首元素地址,arr[9]相当于arr+9,即根据arr首元素的地址向后跳过9个元素。那么现在arr[10]也就相当于arr+10,即向后跳过10个元素。

我们打开内存窗口,继续调试。

接下来我们调试看看是否是这样的。

由此,我们找到了上面这段代码出现死循环的原因。

那么我们从更深层次来解释一下代码出现死循环的原因。

因为程序中的变量i和数组arr是局部变量,而局部变量是在栈区分配中间的。

在栈区中,空间的分配习惯是先使用高地址的空间,再使用低地址的空间。

所以根据程序的执行顺序,在栈区中地址相对高的位置先放置了i,然后在相对地的地址处放置了arr。

按理说当i

但是为什么这时候却不会 错呢/p>

原因是此时程序正忙着执行死循环,还没有空闲下来给我们 错……

可以看到Release版本确实对程序进行了优化。

其实我们的程序在运行的时候,是从一个.c文件进行编译、链接,生成一个.exe的运行文件的。

所以如果产生了编译型错误和链接型错误,程序就无法生成.exe.文件。

而当程序调用函数的时候,是在链接器里面找这个函数,所以当程序找不到该函数的时候,就会 连接错误。

通常链接型错误产生的原因都是符 未定义,或者符 写错了。所以当遇到链接型错误我们应该先找符 名。

运行时错误

当程序执行起来后,却发现执行的结果我们想要的不一样,这时候我们称之为运行时错误。

注:null和Null在习惯上认为是’ ’,而NULL认为是空指针。

然后着手写出我们自己的strcpy函数。

但是能不能再简化呢/p>

看第三个版本~

因为’ ’的ASCII码值是0,所以我们可以这样优化代码:

但是如果我们每次使用指针都要这样写一个检验代码是不是有点麻烦呢/p>

这时候我们就要引入一个宏 – assert

我们还可以这样写:

但是分开写的话代码会更容易理解。

2. 尽量使用const

上面这段代码现在看起来好像已经很好了,但是如果有人非常不小心地在函数中把要拷贝和要被拷贝的字符串地址写反了怎么办呢/p>

我们重新看cplusplus.com里面strcpy的参数:会发现在source类型的前面会有一个const。

这时候有一个女孩p是男孩m的女朋友,记为:int* p=&m。

于是男孩m就决定不给女孩买奶茶,不让女孩拿走男孩m兜里的钱,我们就用const来表示,记为:const *p=&m。

于是女孩就不愿意了,对m说,说:“连秋天的第一杯奶茶都不愿意给我买,我要和你分手!”然后女孩p就甩了男孩m,和男孩n在一起了。记为:int* p=&n。

所以我们现在再回头把模拟字符串拷贝的函数进行优化。

在char* src前面加上const,这样如果不小心把字符串的拷贝写反了,编译时程序就会给我们 错,把错误扼杀在摇篮中。

这个返回值是被拷贝的字符串的地址。

当我们这个代码完成了之后,我们在使用这个函数的时候仍然要注意一些问题:

比如:

  1. 如果目标字符串比被拷贝的字符串长,那么拷贝后打印出来的字符串也不会有原来字符串的内容。

  1. 目标空间应该可以修改

以上所讲的调试方法和代码技巧,你学废了吗

希望下次我们遇到代码问题的时候,不再是凭着感觉该代码。而是先思考问题的所在,然后运用调试的技巧,找出代码的bug,成为一名能写出好代码,又会找bug的程序员!

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年7月24日
下一篇 2021年7月25日

相关推荐