std::array::operator[]的引用说明:



我写了这个小程序来检查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的值时,上述代码为什么没有给出分段错误错误?

最佳答案

>>当idx的值假定为18446744073709551615615时,上述代码为什么没有给出分段错误错误?

因为这个大数是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 []的内存值来进行检查。为此,您可以将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++语言规则并未定义期望从负数组索引中获得的内容,因此编写编译器的人员已获得许可,将适合它们的任何值返回为a [-1]。他们唯一关心的可能是编写不会使而不是降低行为良好的源代码的性能的机器代码。

10-07 18:56
查看更多