当前位置:嗨网首页>书籍在线阅读

06-数组边界

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

10.1.4 数组边界

在使用数组时,要防止数组下标超出边界。也就是说,必须确保下标是有效的值。例如,假设有下面的声明:

int doofi[20];

那么在使用该数组时,要确保程序中使用的数组下标在 019 的范围内,因为编译器不会检查出这种错误(但是,一些编译器发出警告,然后继续编译程序)。

考虑程序清单10.6的问题。该程序创建了一个内含4个元素的数组,然后错误地使用了 -16 的下标。

程序清单10.6  bounds.c 程序

// bounds.c -- 数组下标越界
#include <stdio.h>
#define SIZE 4
int main(void)
{
     int value1 = 44;
     int arr[SIZE];
     int value2 = 88;
     int i;
     printf("value1 = %d, value2 = %d\n", value1, value2);
     for (i = -1; i <= SIZE; i++)
          arr[i] = 2 * i + 1;
     for (i = -1; i < 7; i++)
          printf("%2d %d\n", i, arr[i]);
     printf("value1 = %d, value2 = %d\n", value1, value2);
     printf("address of arr[-1]: %p\n", &arr[-1]);
     printf("address of arr[4]: %p\n", &arr[4]);
     printf("address of value1: %p\n", &value1);
     printf("address of value2: %p\n", &value2);
     return 0;
}

编译器不会检查数组下标是否使用得当。在C标准中,使用越界下标的结果是未定义的。这意味着程序看上去可以运行,但是运行结果很奇怪,或异常中止。下面是使用GCC的输出示例:

value1 = 44, value2 = 88
-1 -1
0 1
1 3
2 5
3 7
4 9
5 1624678494
6 32767
value1 = 9, value2 = -1
address of arr[-1]:    0x7fff5fbff8cc
address of arr[4]:     0x7fff5fbff8e0
address of value1:     0x7fff5fbff8e0
address of value2:     0x7fff5fbff8cc

注意,该编译器似乎把 value2 存储在数组的前一个位置,把 value1 存储在数组的后一个位置(其他编译器在内存中存储数据的顺序可能不同)。在上面的输出中, arr[-1]value2 对应的内存地址相同, arr[4]value1 对应的内存地址相同。因此,使用越界的数组下标会导致程序改变其他变量的值。不同的编译器运行该程序的结果可能不同,有些会导致程序异常中止。

C语言为何会允许这种麻烦事发生?这要归功于C信任程序员的原则。不检查边界,C程序可以运行更快。编译器没必要捕获所有的下标错误,因为在程序运行之前,数组的下标值可能尚未确定。因此,为安全起见,编译器必须在运行时添加额外代码检查数组的每个下标值,这会降低程序的运行速度。C相信程序员能编写正确的代码,这样的程序运行速度更快。但并不是所有的程序员都能做到这一点,所以就出现了下标越界的问题。

还要记住一点:数组元素的编号从0开始。最好是在声明数组时使用符号常量来表示数组的大小:

#define SIZE 4
int main(void)
{
     int arr[SIZE];
     for (i = 0; i < SIZE; i++)
     ....

这样做能确保整个程序中的数组大小始终一致。