昨天晚上,我正在给之前获奖的同学发奖品,正在进行中的时候,看到黄兄推送了一篇文章,里面讲到数组越界会导致无限循环。
关于抽奖看这篇文章《Linux进程管理数据结构》,以后也会不定期抽奖,今年多赚点钱,过年的时候,希望给大家抽一个大奖,喜欢的读者使劲给转发点赞,关注,未来是你们的,大奖也是你们的,感谢支持,不喜欢的请轻拍。过年抽奖我还是很期待的,每年公司的年会抽奖的时候,我总是能小中一把,有一年我还中了个一等奖,不知道大家对一等奖什么概念,中一等奖的概率非常低,集人品运气于一身的,我当时酝酿了全宇宙的力量,不说了,发功的时候真的非常累。
就是这个截图,我看了下,感觉有点意思,赶紧就自己敲下代码看看是怎么回事,可惜的是,我怎么也没看到出现无限循环啊。
代码如下:
#include "stdio.h"
int main()
{
int a[10] , i;
for(i = 1;i<= 10;i++){
a[i] = 0;
printf("%d %d\n",i,a[i]);
}
return 0;
}
我先是在 dev c++ 上面试了下,结果如下
我这时候想,黄兄是不是忽悠我了,我要去成都叫他请我吃饭,然后我就给他评论
评论如下
竟然还有人给我的评论点赞了,啊啊啊啊啊啊~
不死心,赶紧又到Linux 下用 gcc编译 结果如下
还是没有无线循环,我要死了,要是在群里面讨论这个,我要是说不出来会不会丢脸一个晚上。
赶紧用大招,看看i的地址和a[i] 的地址
看这个图,i等于 10的时候, &a[10] 的地址和 i的地址还是独立的,虽然 a[10]已经超过了 数组的范围,但是并没有对i造成任何影响。
然后,我脑子一响,突然顿悟发现这个代码的问题所在了,这个数组越界没有越过去,要是越过去了,侵犯了i的领土「内存地址」了肯定就死循环了。
代码修改一下
#include "stdio.h"
int main()
{
int a[10] , i;
for(i = 1;i<= 12;i++){
a[i] = 0;
printf("%d %d\n",i,a[i]);
}
return 0;
}
看看内存分布
上图中 i = 10的时候,&a[10] 还没有越界到 &i「i的地址」,等下一次循环 i = 11的时候,那 &a[11] 就等于 &i 了,a[11] = 0,其实也就是 i= 0,死循环也就是这样来了。
从这个例子我们可以明白一点,为什么数组不能越界操作了没?如果真的是项目里面的代码,要是编译器也不给出提示,那么你即将构造一个非常难排查的bug出来。
如果对上面的地址还是不感冒,可以看看这个图片
a我们定义的是10的大小,但是我们使用 a[10],这个是越界操作,越界操作编译器不会提示编译出错,但是这样操作就可能侵犯到i的地址,实际上把i给赋值了,所以会导致i=0,一直跳不出for循环。
我们正常一个数组遍历会这样写
#include "stdio.h"
int main()
{
int i;
int a[10];
for(i = 0;i < sizeof(a)/sizeof(a[0]);i++){
a[i] = 0;
printf("%d %d %p %p\n",i,a[i],&i,&a[i]);
}
return 0;
}
编译器就好像法律,约束着人们的行为,但是有些法律没有涉及到的,就很容易被打擦边球。
后用这个代码看看内存的增长「一个读者奉献的」
#include <stdio.h>
int main(void)
{
#pragma pack (4)
int j;
int a[10];
int i;
#pragma pack()
printf("a=%p &a[10]=%p &i=%p &a[-1]=%p &a[-2]=%p &j=%p\r\n",a, &a[10],&i,&a[-1],&a[-2],&j);
return 0;
}