内存越界与相邻内存覆盖问题
先上一段代码:
int i;
int a[10];
for(i=0; i<=10; i++)
{
a[i] = 0;
}
根据ANSI C标准规定:数组中实际不存在的“溢界”元素的地址位于数组所占内存之后,可以对这个地址赋值和比较,但是不能引用该元素。
在我的机器上,得到:
&i = 0x22ff4c
&a[10] = 0x22ff38
&a[15] = 0x22ff4c
可以看出,在这机器上,编译器按照内存地址递减的方式来给变量分配内存,变量i在数组a之后,按以上结果来看,编译器给数组多分配5个元素的内存,但是,如果在某些机器上,编译器严格按需分配,那么对a[10]操作就是对变量i操作,这并不是我们所需要的。此时,本来循环计数器i的值为10,循环体内将不存在的a[10]设置为0,实际上却是将计数器i的值设置为0,这就陷入了一个死循环。如下图所示:
再看一例:
main()
{
int i;
char c;
for(i=0; i<5; i++)
{
scanf(“%d”, &c);
printf(“%d”, i);
}
printf(“\n”);
}
这个程序从标准设备读入5个数,在标准输出设备上写5个数,在我的机器上,效果如下:
0 1 2 3 4
0 0 0 0 0 0 1 2 3 4
0 0 0 0 0 0 1 2 3 4
0 0 0 0 0
黄色的是输入,将这样一直循环下去。为什么呢?这里c被声明为char类型,而不是int类型。然而scanf函数并不能分辨接受的是int*(指针)还是char*(指针),只是将这个指针当成是指向整数的指针来接受,并把指针指向的位置存储一个整数。但是,整数所占的存储空间要大于字符所占的存储空间,所以字符c附近的内存将被覆盖。
如果编译器按照内存地址递减的方式来给变量分配内存,整数变量i在字符变量c之后,因此每次读入一个数值到c时,i的低位部分被覆盖为0,i的高位部分本来为0,所以变量i为0,相当于每次i都被重设为0,一直循环。如下图所示:
在我的机器上,得到:
&c = 0x22ff53
&i = 0x22ff54
总结一下:
两个例子的原理一样,都是由于内存的“溢出”对相邻内存的覆盖,只是他们的表现的形式不同。它们有一个相似之处,能通编译器,却对程序造成致命伤害,都是难于发现的bug。