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]。他们唯一关心的可能是编写不会降低正常源代码性能的机器代码。
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]。他们唯一关心的可能是编写不会降低正常源代码性能的机器代码。