C和Java处理越界数组访问的方式有何不同?

Java中 ,我们在尝试访问和数组越界时得到一个漂亮的exception,但在C中并非如此:

#include  int main() { int x[10] = {1,2,3}; // After the third index, I know that the rest are 0. int i = 0; while (i<99) { // Here I exceed the size of the array, printf("%d",x[i]); // printing non-existent indexes. i++; } return 0; } 

输出是:

 12300000001021473443840268687241986531780216078041842686812-12686816199941806157 0338438-2199933010019993299657804184019993359001214734438441990611-819265930944- 8192925345321-122881852697619690479012147344384268694020027537742147344384145346 587600214734438400102686884-819226869482003047601567625672026869562002753732-120 02706633004199040214734438402020893505321130682200320201752380100000243875924666 4687366080-21479789413447414207980 Process returned 1 (0x1) execution time : 0.719 s Press any key to continue. 

从技术上讲,究竟发生了什么? 它不完全是“int size overflow”,对吗?

C外部访问数组的边界是未定义的行为 ,结果是不可预测的,它可能看起来工作正常,它可能是seg-fault等… Annex J.2 C99标准草案 未定义的行为并列出以下内容子弹:

数组下标超出范围,即使一个对象显然可以使用给定的下标访问(如左边的表达式[1] [7],给出声明int a [4] [5])(6.5.6)。

6.5.6条第8款,其中规范性详细说明。

另一方面, Java语言规范 部分10.4使得访问数组超出范围是一个例外:

在运行时检查所有数组访问; 尝试使用小于零或大于或等于数组长度的索引会导致抛出ArrayIndexOutOfBoundsException

JavaC有不同的设计理念。 Java摒弃了避免未定义行为的方法,而CC ++为实现提供了自由。 关于此的一些好文章:

  • 为什么语言设计师容忍未定义的行为
  • 未定义行为背后的哲学
  • Java中未定义的行为

只是为了补充Shafik已经说过的话:

从技术上讲,究竟发生了什么? 它不完全是“int size overflow”,对吗?

该数组占用10 * sizeof(int)字节的内存。 当您在超出此大小边界的索引处打印数组值时,您实际上正在访问尚未为该数组分配的内存部分。 它可能是任何东西。 它可能是其他变量的值; 它可能是指示; 它可能是您没有业务访问的受保护内存。

这不是int大小溢出,而是内存访问溢出。 当你在内存中声明x[10]时,它将分配地址并存储0 10次:

 x[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 

当你宣布:

 x[10] = {1, 2, 3} 

它被指定为{1, 2, 3, 0, 0, 0, 0 , 0, 0 , 0}但是您正在打印超出已分配的内存,因此可以存储任何不在您程序中的内容。

我运行你的程序,并打印x[i]s地址和值,我可以看到地址和值,如:

 x[0] = 0x7fff4b1b6290: 1 x[1] = 0x7fff4b1b6294: 2 x[2] = 0x7fff4b1b6298: 3 x[3] = 0x7fff4b1b629c: 0 x[4] = 0x7fff4b1b629c: 0 x[5] = 0x7fff4b1b62a0: 0 x[6] = 0x7fff4b1b62a4: 0 x[7] = 0x7fff4b1b62a8: 0 x[8] = 0x7fff4b1b62ac: 0 x[9] = 0x7fff4b1b62b0: 0 x[10] = 0x7fff4b1b62b4: 0 x[11] = 0x7fff4b1b62b8: 11 x[12] = 0x7fff4b1b62bc: 293 x[13] = 0x7fff4b1b62c0: 0 x[14] = 0x7fff4b1b62c4: 0 x[15] = 0x7fff4b1b62c8: 212348189 

所以你可以看到直到x[9]你得到正确的输出,但之后它只会打印下一个地址的值,这是随机值。

在计算机编程中,边界检查是在使用变量之前检测变量是否在某些边界内的任何方法。 它通常用于确保数字适合给定类型(范围检查),或者用作数组索引的变量是否在数组的范围内(索引检查)。 失败的边界检查通常会导致产生某种exception信号。

请参阅: http : //lelanthran.com/deranged/?p = 182

设计C的人希望语言尽可能快速和灵活,因此运行时就像程序员所说的那样,即使它具有破坏性。 在Java中,设计人员保护程序员不要做一些愚蠢的事情,因此每次访问数组时,它首先检查访问是否在边界内。

一种方法不一定比另一种更好,但每种方法在做不同的事情时都更好。