在下面的代码中,我尝试访问数组的'-1'元素,我没有得到任何运行时错误。
#include <stdio.h>
int A[10] = {0};
int main(){
A[-1] += 12;
printf("%d",A[-1]);
return 0;
}
当我运行代码时,它输出12
,这意味着它正在向不存在的A [-1]添加12。直到今天,每当我试图访问越界元素时,我都遇到了运行时错误。我之前从未尝试过一个简单的代码。
有谁可以解释为什么我的代码成功运行?
我在计算机和ideone上运行它,在两种情况下都成功运行。
答案 0 :(得分:1)
C和C ++没有任何边界检查。它是语言的一部分。它是为了使语言更快地执行。
如果您想要边界检查,请使用另一种语言。 Java也许?
当你的代码执行时,你很幸运。
答案 1 :(得分:1)
在C ++(和C)中,数组不检查范围索引。他们不是上课。
在C ++ 11中,您可以使用std::array<int,10>
和at()
函数:
std::array<int,10> arr;
arr.at(-1) = 100; //it throws std::out_of_range exception
或者您可以使用std::vector<int>
和at()
成员函数。
答案 2 :(得分:1)
你看,当你分配一个像这样的变量时,它会落在堆栈上。 Stack在您调用的每个函数中保存有关局部变量的小包信息,用简单的单词表示。运行时能够检查是否超出了已分配堆栈的范围,但是如果在堆栈中的无效位置写入某些数据则无法检查。堆栈可能如下所示:
[4个字节 - 一些ptr] [4个字节 - A&#39的第一个元素] [4个字节 - A&#39的第二个元素] ...
当您尝试分配给数组的第-1个元素时,实际上是尝试读取数组前面的四个字节(四个字节,因为它是一个int数组)。您覆盖了堆栈中保存的一些数据 - 但这些数据仍处于有效进程的内存中,因此系统没有任何投诉。
尝试在Visual Studio中以发布模式运行此代码:
#include <stdio.h>
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 - 我猜,内存管理器在堆栈帧之间插入一些哨兵数据,以捕获缓冲区溢出/欠载等错误。