【问题标题】:Why can I access array indexes greater than array's size in C++? [duplicate]为什么我可以在 C++ 中访问大于数组大小的数组索引? [复制]
【发布时间】:2013-11-13 19:13:02
【问题描述】:

我正在编写代码并意识到我可以“访问”数组中与数组大小相同或更大的索引的元素。为什么这不会产生错误?

例如,

#include <iostream>
using namespace std;

int main ()
{
    int b_array[5] = {1, 2, 3, 4, 5};


    cout << b_array[5]  << endl  // Returns 0
         << b_array[66] << endl; // Returns some apparently random value.

    return 0;
}

【问题讨论】:

  • 未定义的行为。任何事情都有可能发生。
  • 这是未定义的行为。运行时发生的任何事情都是错误。
  • @ThisIsNotAnId,因为它需要为您可能不会使用的东西付费。如果你想要边界检查,你可以自己做。
  • 这是设计使然。这是 C++ 编译器和 C 运行时检查的众多事情之一。如果您想要更安全的语言,请尝试 C#、Java 或其他更高级别的语言。
  • 标准/编译器注释

标签: c++ arrays


【解决方案1】:

唯一的技术答案是“因为 C++ 语言规范这么说”。访问越界值是未定义的行为。你的个人品味无关紧要。

在“未定义的行为”(C++ 规范中有很多)背后,需要让编译器开发人员根据他们必须运行的平台实现不同的优化。

如果您认为索引经常在循环中使用,如果您检查边界,您最终会检查每次迭代,总是成功(从而浪费处理器时间)。

【讨论】:

    【解决方案2】:

    由于会导致性能损失,C++ 不实施边界检查。
    例如,vector 模板包含一个检查边界的 at() 函数,但比 [] 运算符慢约 5 倍。
    低级语言往往会迫使程序员编写安全且无错误的代码以换取高性能。

    【讨论】:

      【解决方案3】:

      尽管有像您这样的简单情况,编译器和/或静态分析器可以检测到访问超出范围,但通常在编译时执行此操作是不可行的。例如,如果您将数组传递给一个函数,它会立即衰减为一个指针,并且编译器没有机会在编译时进行边界检查。

      运行时边界检查的替代方法相对昂贵:对每次访问进行检查会将简单的内存取消引用变成潜在的停滞分支。更难的是,您可以在指针上使用取消引用运算符,也就是说,您甚至无法轻易知道在哪里找到实际数组对象的大小。

      因此,越界数组访问的行为是故意未定义:系统可以跟踪这些访问,但它不必 。此外,系统在越界数组访问时实际执行的操作也未指定,即它可以根据上下文执行不同的操作。在许多情况下,它只会返回垃圾,而这并不是很有用。但是,尤其是在适当的调试设置下,系统可能会在检测到违规时改为assert()

      【讨论】:

        【解决方案4】:

        C++ 允许直接访问您的程序的内存。没有为您完成边界检查。这可能会导致非常讨厌的错误,但与其他“更安全”的语言相比,它也非常有效。

        数组只不过是指向内存位置的指针。您尝试访问的索引,例如array [66] 中的索引66,通过将66 * sizeof(int) 添加到数组的起始地址来解析。最终计算出的地址是否在一定范围内,超出了编译器的检查范围。

        换句话说,array [i] 与 C++ 中的 *(array + i) 相同。其实你可能会惊讶array [i]也可以写成i [array]

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-28
          • 1970-01-01
          • 1970-01-01
          • 2020-04-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多