【问题标题】:Why don't I get a runtime error when I access an out-of bounds element of an array?当我访问数组的越界元素时,为什么不会出现运行时错误?
【发布时间】:2013-01-16 09:56:38
【问题描述】:

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

#include <stdio.h>

int A[10] = {0};

int main(){

    A[-1] += 12;

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

    return 0;
}

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

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

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

【问题讨论】:

  • 未定义的行为。如果A 不是第一个元素的指针,你可以,但这更像是负索引而不是越界。
  • 我也尝试访问A[11],运行成功。
  • @A.06:是的。 “未定义的行为”包括“它运行成功”。未定义的行为还包括“可怕的崩溃”和“独角兽从你的电脑屏幕上飞出”。它字面意思意味着你无法以任何可靠性来推理它的行为,这就是为什么 UB 正是你不想想要做的事情。

标签: c++ c arrays runtime-error indexoutofboundsexception


【解决方案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;
}

编辑:响应 cmets。

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

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

【讨论】:

  • 除了定义int A[10] = {0};(在OP的代码中)在任何函数的主体之外,所以我怀疑A在这种情况下是在堆栈上创建的:)
  • 啊,你说得对。但是我想,这仍然是在进程内存中写入的问题,是什么让这段代码不会崩溃。
  • @Spook,这是一个合理的解释。只是不要指望这是原因。可能只是编译器心情不好:)
【解决方案2】:

在 C++(和 C)中,数组不会检查超出范围的索引。它们不是类。

在 C++11 中,您可以将 std::array&lt;int,10&gt; 和 at() 函数用作:

std::array<int,10> arr;

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

或者你可以使用std::vector&lt;int&gt;和at()成员函数。

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 如果你想要边界检查,使用合适的容器和at()。
    • 但是当 A[-1] 不存在时,它如何将 12 添加到 A[-1] 中?
    • @A.06,它仍然存在于内存中,但试图推测未定义行为的作用是毫无意义的。
    • @chris 但是我之前在 C/C++ 中访问越界元素时遇到运行时错误。
    • @A.06,同样,未定义的行为。它不必是一致的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 2020-07-16
    • 1970-01-01
    • 2015-09-23
    • 1970-01-01
    • 2019-07-17
    相关资源
    最近更新 更多