C语言调试示例

今天运用调试解决了一个bug,想和大家分享一下:
程序如下:

#include 

int main()
{
 int i = 0;
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 for(i=0; i<=12; i++)
 {
 arr[i] = 0;
 printf("hehe\n");
 }
 return 0;
}

大家可以思考一下在VS2022、Debug、X86环境下运行以上代码的结果是什么,并跑一下程序,看看运行结果与自己预料的结果是否一样。

在跑代码以前,很多小伙伴大概会以为运行结果就是打印12次"hehe"字符串,但是程序跑出来的结果却是死循环,运行结果如下:
C语言调试示例_第1张图片

那么很多很多小伙伴就会思考:为什么运行结果是个死循环呢,为了接诶巨额这个问题,我们就要运用调试来解决了。
再循环处设置断点,开始调试,并进行监视
C语言调试示例_第2张图片
C语言调试示例_第3张图片
输入要进行监视的变量:
C语言调试示例_第4张图片
单步执行,i<=11的时候变量的值都正确,我们重点看i=12的时候对应的变量变化。
C语言调试示例_第5张图片
C语言调试示例_第6张图片
我们可以发现,当i=12循环结束的时候,执行arr[i] = 0;语句之后,i的值变成了0,依旧满足循环条件,无法跳出循环,造成了死循环,那么为什么在执行完arr[i]=0之后,i的值就变为0了呢?为了解决这个问题,我们就要找到i的地址了
C语言调试示例_第7张图片
监视i的地址,我们发现i与数组首地址差了48,也就是12个int型数据的大小,而arr[12]地址与arr首地址也差了12个int型数据的大小,因此,我们再监视arr[12]的地址。
C语言调试示例_第8张图片
我们发现,arr[12]与i的地址一样,对arr[12]进行修改就是对i进行修改,所以会造成死循环。

到此为止,为什么会造成死循环我们解决了,但是还有一个问题,那么就是为什么i的地址与arr[12]的地址一样吗,这只是巧合吗?

在查阅了一些资料后,我明白了其中的原因。
在VS2022、Debug、X86环境下的栈内存中,默认先使用高地址的内存空间,再使用低地址的内存空间,同时数组随着下标的增长,地址是由低到高进行变化的,以本程序为例,为arr数组和i分配的空间示意图如下:
C语言调试示例_第9张图片
由图示可以看出,arr和i的内存分布并不是仅仅挨在一起的,而是中间隔了一段距离,至于中间隔几个距离,这个就与编译器有关,VS2022、Debug、X86之间是2个int型,在Linux中,就隔了1个int型,所以越界访问后就造成了死循环。

当然,如果将环境换成Debug、X64或者Release,或者将arr数组的定义在前,i的定义在后,那么将会打印13个"hehe"并提示数组越界错误。
C语言调试示例_第10张图片

这段程序最本质的错误就是数组越界,我们在写程序的过程中,一定不要数组越界,否则可能会造成无法预知的错误。

从这个例子中我们可以看到调试的重要性,在遇到一些bug时,如果我们不进行调试,那么将很难发现其中的错误。

你可能感兴趣的:(c语言)