今天课的内容是关于实用调试技巧
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;
}
这个代码运行的结果是 : 死循环,一直打印 hehe
首先,这个代码有明显的越界访问内存的错误,但是它并没有报错或者崩溃。
经过调试发现,当我的arr数组中每个元素都被替换成了0之后,i来到了10,进入for循环,紧接着它会把arr[10]也赋值为0,直到arr[12]时,arr[12]=0的同时,i也变成了0
复习一个调试的技巧
当发现问题时,我需要来到arr[10]处,观察越界后程序的走向,
arr[i] = 0; 只需要在这行代码用F9添加一个断点,然后右键设置条件 i==10
按F5可以直接来到满足条件的断点处。
监视窗口发现,当来到arr[10],arr[11]时,它们未被初始化定义,也未被赋值为0的时候,他们都是随机值。
而arr[12]未被赋值时,它的值是12,与i相同。
赋值后,arr[12]变为0,同时 i 也变成了0,这就是死循环的原因所在,在本该跳出循环的时候,i=0,就又满足了进入for循环的条件,而当i++到12时,它又会重新变为0。
而通过地址发现,i和arr[12]的地址是相同的。
原理解释
arr 和 i 都是局部变量,局部变量是放在栈区
栈区的使用原则是,先使用高地址空间,再使用低地址空间
arr是数组,数组中元素的地址随着下标从低到高
代码中,i是先被定义的,被放在了高地址空间,arr被放在相对低地址空间,随着元素下标的上升,地址也会越来越高,越界访问后,就有可能访问到 i 所在的地址,将它赋值。这里到底会不会访问到i,什么时候访问到i,会受到编译环境的影响。我这里使用的是vs2019。
当 arr先被定义时,不会出现死循环,但是运行时程序会崩溃。因为在循环结束后,会发生越界访问报错。
而上述代码会因为进入死循环中而没有机会报错。