0

在下面的这段代码中,我尝试访问数组的第 '-1' 个元素,但没有收到任何运行时错误。

#include <stdio.h>

int A[10] = {0};

int main(){

    A[-1] += 12;

    printf("%d",A[-1]);

    return 0;
}

当我运行代码时,它输出12这意味着它将 12 添加到不存在的 A[-1]。直到今天,每当我尝试访问越界元素时,都会遇到运行时错误。我以前从未在简单的代码上尝试过。

谁能解释为什么我的代码运行成功?

我在我的电脑和ideone上运行它,在这两种情况下它都运行成功。

4

3 回答 3

1

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

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

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

于 2013-02-01T05:34:54.250 回答
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()成员函数。

于 2013-02-01T05:38:48.477 回答
1

你看,当你像这样分配一个变量时,它会落在堆栈上。堆栈包含有关您调用的每个函数中的局部变量的小信息包,用简单的话来说。运行时能够检查您是否超出了分配的堆栈的范围,但不能检查您是否在堆栈的无效位置写入了一些数据。堆栈可能如下所示:

[4 字节 - 一些 ptr][4 字节 - A 的第一个元素][4 字节 - A 的第二个元素] ...

当您尝试分配给数组的第 -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 - 我猜,内存管理器在堆栈帧之间插入一些哨兵数据以捕获缓冲区溢出/欠载等错误。

于 2013-02-01T05:38:58.710 回答