1)越界
C语言数组是静态的,不能自动扩容,当下标小于零或大于等于数组长度时,就发生了越界,访问到数组以外的内存。
调试以下代码
#include <stdio.h>
int main()
{
int a[3] = { 10,20,30 }, i;
for (i = -2;i <= 4;i++)
{
printf("a[%d]=%d\n",i,a[i]);
}
return 0;
}
可得到结果如下
越界访问的数组元素的值是不确定的,没有实际含义。我们并不知道数组之外的内存是什么,可能是其它变量的值、函数参数、地址,这些都不可控。
如果我们对该内存有使用权限,当发生数组越界,程序将正常运行,但会出现不可控的结果(如上例所示);如果我们对该内存没有使用权限,或者该内存没有被分配,那么程序将会崩溃。
再看一个例子
#include <stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
*p = 20;
return 0;
}
这里test函数在执行后被销毁,变量a也被销毁,而在main函数中仍对a的地址进行了访问,而此时原变量a的地址所指向的内存值已改变。此例和数组越界访问原理一致。
2)溢出
当数组的元素个数超过数组长度时,就会发生溢出。如下所示:
int a[3] = {1, 2, 3, 4, 5};
数组长度为3,却初始化了5个元素,超出了数组容量,所以只能保存前3个元素,后面的元素被丢弃。
数组溢出还常见于字符串。调试以下代码
#include <stdio.h>
int main()
{
char arr[5] = "according";
puts(arr);
return 0;
}
可到到结果如下
当字符串的长度大于数组长度时,数组只能取字符串前面的一部分,也就是"accor",即使编译器在最后添加了'\0',它也不会保存到数组里,所以 printf() 扫描数组时不会遇到结束符'\0',只能继续向后扫描。
而后面内存中的数据我们不知道是什么,字符能否识别,何时遇到'\0',这些都是不确定的。当字符无法识别时,就会出现乱码,显示奇怪的字符。
在用字符串给字符数组赋值时,要保证数组长度大于字符串长度,以容纳结束符'\0'。