std::array 和运算符 []

std::array and operator []

std::array::operator[] 的参考说明:

Returns a reference to the element at specified location pos. No bounds checking is performed.

我写了这个小程序来检查 operator[]:

的行为
#include <array>
#include <cstddef>
#include <iostream>
using std::array;
using std::size_t;
using std::cout;
using std::endl;

#define MAX_SZ 5

int main (void){

    array<int,MAX_SZ> a;
    size_t idx = MAX_SZ - 1;

    while(idx < MAX_SZ){
        cout << idx << ":" << a[idx] << endl; 
        --idx;
    }
    cout << idx << ":" << a[idx] << endl; 

    return 0;
}

编译并 运行 时,上述程序产生以下输出:

4:-13104
3:0
2:-12816
1:1
0:-2144863424
18446744073709551615:0

根据以上输出,我的问题是:

idx 的值假定为 18446744073709551615 时,为什么上面的代码没有给出分段错误?

oprator[] 不需要进行边界检查。因此,它是越界访问。越界访问会导致未定义的行为。意味着任何事情都可能发生。我真的没有任何意思。对于前。它可以点披萨。

如前所述,您面临未定义的行为。 不过,如果您喜欢 boundary check,您可以使用 .at() 而不是运算符 []。这会有点慢,因为它会在每次访问数组时执行检查。

内存检查器(例如 valgrind)也能够在程序运行时发现此类错误。

>> 当 idx 的值为 18446744073709551615 时,为什么上面的代码没有给出分段错误?

因为这个大数是2**64 - 1,也就是2的64次方,减1。

而就数组索引逻辑而言,这与-1完全一样,因为2**64值超出了64位硬件可以考虑的范围。所以你正在(非法)访问 a[-1],而它恰好在你的机器中包含 0。

在你的记忆中,这是a[0]之前的单词。它是堆栈中的内存,硬件完全允许您访问,因此预计不会发生分段错误。

您的 while 循环使用 size_t 索引,它本质上是一个无符号的 64 位数量。因此,当索引递减并从 0 变为 -1 时,循环控制测试将 -1 解释为 18446744073709551615(由 64 位全部设置为 1 组成的位模式),这比 MAX_SZ = 5,所以测试失败,while循环就此停止。

如果你对此有任何疑问,你可以通过控制数组a[]周围的内存值来检查。为此,您可以 "sandwich" 在 2 个较小的数组之间排列 a,比如 magica 和 magicb,您可以正确初始化它们。像这样:

#include <array>
#include <cstddef>
#include <iostream>
using std::array;
using std::size_t;
using std::cout;
using std::endl;

#define MAX_SZ 5

int main (void){

    array<int,2> magica; 

    array<int,MAX_SZ> a;
    size_t idx = MAX_SZ - 1;

    array<int,2> magicb; 

    magica[0] = 111222333;
    magica[1] = 111222334;

    magicb[0] = 111222335;
    magicb[1] = 111222336;

    cout << "magicb[1] : " << magicb[1] << endl;

    while (idx < MAX_SZ) {
        cout << idx << ":" << a[idx] << endl; 
        --idx;
    }
    cout << idx << ":" << a[idx] << endl; 

    return 0;
}

我的机器是基于 x86 的机器,因此它的堆栈向数值较低的内存地址增长。数组 magicb 在源代码顺序中定义在数组 a 之后,因此它在堆栈中最后分配,因此它的地址在数值上低于数组 a.

因此,内存布局为:magicb[0], magicb[1], a[0], ..., a[4], magica[0], magica[1]。所以当你请求 a[-1].

时,你期望硬件给你 magicb[1]

确实是这样:

magicb[1] : 111222336
4:607440832
3:0
2:4199469
1:0
0:2
18446744073709551615:111222336

正如其他人指出的那样,C++ 语言规则没有定义您期望从负数组索引中得到什么,因此编写编译器的人有权 return 任何适合的值他们作为[-1]。他们唯一关心的可能是编写不会降低正常源代码性能的机器代码。