std :: array和运算符[]

时间:2019-07-29 09:34:12

标签: c++ operators stdarray

std::array::operator[]的引用指出:

  

返回对在指定位置pos的元素的引用。没有   进行边界检查。

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

3 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:1)

如前所述,您面临不确定的行为。 不过,如果您想拥有boundary check,则可以使用.at()代替运算符[]。这会稍慢一些,因为它会检查对数组的每次访问。

此外,诸如valgrind之类的内存检查器也可以在程序运行时找到类似的错误。

答案 2 :(得分:0)

>>当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 []周围的内存值来进行检查。为此,您可以将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 ]。他们唯一关心的可能是编写不会不会降低性能良好的源代码的性能的机器代码。