我正在编写代码并意识到我可以“访问”与数组大小相同或更大索引的数组元素。为什么这不会产生错误?
例如,
#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;
}
答案 0 :(得分:6)
唯一的技术答案是“因为C ++语言规范说的”。访问越界值是未定义的行为。你的个人品味无关紧要。
在“未定义的行为”背后(C ++规范中有很多),需要让编译器开发人员根据他们必须运行的平台实现不同的优化。
如果你认为索引经常在循环中使用,如果你检查边界,你最终会检查每次迭代,总是成功(因此浪费处理器时间)。
答案 1 :(得分:4)
由于产生的性能损失,C ++没有实现边界检查
例如,vector
模板包含at()
函数,该函数检查边界,但比[]
运算符慢〜5倍。
低级语言倾向于迫使程序员生成安全且无错误的代码以换取高性能。
答案 2 :(得分:3)
尽管有一些简单的情况,例如编译器和/或静态分析器可以检测到访问超出范围的情况,但在编译时通常不能这样做。例如,如果将数组传递给函数,它会立即衰减为指针,编译器无法在编译时进行边界检查。
替代的运行时边界检查比较昂贵:对每次访问进行检查会将简单的内存取消引用变为可能停滞的分支。为了使事情变得更加困难,你可以在指针上使用dereference运算符,即你甚至不知道在哪里找到实际数组对象的大小。
因此,有意识地进行了越界数组访问的行为 undefined :系统可以跟踪这些访问,但它不会必须。此外,没有指定系统实际对越界数组访问所做的事情,即,它可以根据上下文做不同的事情。在许多情况下,它只会返回垃圾,这实际上并不太有用。但是,特别是在适当的调试设置下,系统可能会在检测到违规时assert()
。{/ p>
答案 3 :(得分:3)
C ++允许直接访问您的程序。没有为您完成边界检查。这可能是非常讨厌的错误的原因,但与其他“更安全”的语言相比,它也非常有效。
数组只是指向内存位置的指针。您尝试访问的索引(例如array [66]
中的索引66)通过将66 * sizeof(int)
添加到数组的起始地址来解决。最终计算的地址是否在某些范围内是超出了编译器检查的内容。
换句话说,array [i]
与C ++中的*(array + i)
相同。事实上,您可能会感到惊讶array [i]
也可以写成i [array]
!