当我访问数组的超出边界元素时,为什么不会出现运行时错误?

在下面的代码中,我尝试访问数组的’-1’元素,我没有得到任何运行时错误。

#include  int A[10] = {0}; int main(){ A[-1] += 12; printf("%d",A[-1]); return 0; } 

当我运行代码时,它输出12表示它正在向不存在的A [-1]添加12。 直到今天每当我试图访问越界元素时,我都遇到了运行时错误。 我以前从未尝试过简单的代码。

任何人都可以解释为什么我的代码成功运行?

我在我的计算机上运行它,也在ideone上运行它,在它成功运行的两种情况下。

你看,当你分配这样的变量时,它会落在堆栈上。 Stack在您调用的每个函数中保存有关局部变量的小包信息,用简单的单词表示。 运行时能够检查是否超出了已分配堆栈的范围,但是如果在堆栈中的无效位置写入某些数据则无法检查。 堆栈可能如下所示:

[4个字节 – 一些ptr] [4个字节 – A的第一个元素] [4个字节 – A的第二个元素] ……

当您尝试分配给数组的第-1个元素时,实际上您尝试读取数组前面的四个字节(四个字节,因为它是一个int数组)。 您覆盖堆栈中保存的一些数据 – 但这仍然在有效进程的内存中,因此系统没有任何抱怨。

尝试在Visual Studio中以发布模式运行此代码:

 #include  int main(int argc, char * argv[]) { // NEVER DO IT ON PURPOSE! int i = 0; int A[5]; A[-1] = 42; printf("%d\n", i); getchar(); return 0; } 

编辑:回应评论。

我错过了一个事实,即A是全球性的。 它不会被保存在堆栈中,而是(通常可能)在二进制模块的.data段中,但其余的解释是:A [-1]仍在进程的内存中,因此赋值不会引发AV。 但是,这样的赋值将覆盖某些东西,即在A之前(可能是指针或二进制模块的其他部分)导致未定义的行为。

请注意,我的示例可能有效,可能不会,具体取决于编译器(或编译器模式)。 例如,在调试模式下,程序返回0 – 我猜,内存管理器在堆栈帧之间插入一些哨兵数据以捕获缓冲区溢出/欠载等错误。

C和C ++没有任何边界检查。 它是语言的一部分。 它是为了使语言更快地执行。

如果您想要边界检查使用另一种具有它的语言。 Java也许吧?

当你的代码执行时,你很幸运。

在C ++(和C)中,数组不检查范围索引。 他们不是上课。

但是在C ++ 11中,你可以使用std::arrayat()函数:

 std::array arr; arr.at(-1) = 100; //it throws std::out_of_range exception 

或者您可以使用std::vectorat()成员函数。