1

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

例如,

#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;
}
4

4 回答 4

7

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

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

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

于 2013-11-02T20:08:00.567 回答
5

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

于 2013-11-02T20:00:18.577 回答
3

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

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

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

于 2013-11-02T20:08:47.337 回答
3

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

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

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

于 2013-11-02T20:09:31.440 回答