一、笔试原题
题目:在Linux x86 _ 54 gcc环境下,下面的程序会出现什么问题?运行结果是什么?为什么?
程序如下图:
通过在gcc的环境下编译运行,发现运行结果为不断死循环打印0-17的数字
我们可以看到这段代码很明显有数组访问越界的问题,但理论上说当 i==18后,进入循环的条件不满足,理应会停止循环,为何会产生死循环的结果那?
二、探讨原因
我们先看一段在VS2019编译环境下的代码的运行结果:
代码如下:
int main() {int i;int arr[10];for (i = 0; i <= 12; i++){arr[i] = 0;printf("%d",i);}return 0;
}
运行结果为:
可以看到,结果也是同样的死循环,两段代码虽然不同但有共同之处,我们对这段代码进行调试寻找答案:(关于调试技巧,可以查看博主这篇文章实用编程调试技巧)
调试步骤
这是数组临界 i==15 时的变量信息:
此时一切正常,arr[16]与[17]因为越界被赋予随机值
当数组越界 i==16、i==17时的变量信息:
我们发现虽然越界了,但是arr[16]、arr[17]的值竟然也被赋值为0,因为我们大胆推测arr[18],应该也是初值为随机值,执行一次循环体后被赋值为18
在调试监视窗口加入arr[18]的信息:
但是事情并不像我们所想的那样,arr[18]的初值并不是随机值,而是等于17,与i相等,为了验证不是巧合,我们修改值再做比较(同样是循环体条件的数值和数组大小值差2)
修改相关值验证上方arr[18]=i不是巧合:
发现这段代码arr[12]也=i,所以这不是巧合
找到代码中死循环的触发原因:
当给arr[18]赋值为0时,i的值也相应改为0,因为可以再次满足循环条件,重复执行循环语句
三、解释原因
arr[18]和i的值共同改变,可以推测出二者应该是在内存中占用了同一个地址
通过查阅相关资料后我们明白:
1、i和arr是局部变量,局部变量是放在栈区上的,而栈区内存的使用习惯是先使用高地址空间,再使用低地址空间
2、数组随着下标的增长地址由低到高变化
如下图所示:
如果i存放的地址每次都与数组最后一个元素的地址差2个空间,那么就会造成VS2019编译环境下的那段代码造成死循环。
而每次都差2个空间并不是巧合,是因为不同编译环境下i存放的地址与arr相差多少空间是固定的(由编译器自己设定):
VC6.0中,会空0个整型
gcc中,会空1个整型
VS2013/VS2019,会空2个整型
而原题是在gcc的环境中编译运行代码,i的地址与a的地址间会空一个长整型,造成死循环(原因与上方VS2019中的代码完全类似):