使用2D阵列的reverse_iterator怪异行为



我有一个2D数组。按正向顺序迭代行是完全可以的,但当我按反向顺序迭代时,它就不起作用了。我不明白为什么。

我使用的是MSVC v143和C++20标准。

int arr[3][4];
for (int counter = 0, i = 0; i != 3; ++i) {
for (int j = 0; j != 4; ++j) {
arr[i][j] = counter++;
}
}
std::for_each(std::begin(arr), std::end(arr), [](auto const& row) {
for (auto const& i: row) {
fmt::print("{} ", i);
}
fmt::print("n");
});
std::for_each(std::rbegin(arr), std::rend(arr), [](auto const& row) {
for (auto const& i: row) {
fmt::print("{} ", i);
}
fmt::print("n");
});

第一个for_each的输出良好:

0 1 2 3
4 5 6 7
8 9 10 11

然而,第二个是垃圾:

-424412040 251 -858993460 -858993460
-424412056 251 -858993460 -858993460
-424412072 251 -858993460 -858993460

当我打印他们的地址时,我无法理解:

<Row addr=0xfbe6b3fc58/>
0 1 2 3
<Row addr=0xfbe6b3fc68/>
4 5 6 7
<Row addr=0xfbe6b3fc78/>
8 9 10 11
<Row addr=0xfbe6b3fb98/>
-424412040 251 -858993460 -858993460
<Row addr=0xfbe6b3fb98/>
-424412056 251 -858993460 -858993460
<Row addr=0xfbe6b3fb98/>
-424412072 251 -858993460 -858993460

这里发生了什么?

这很可能是MSVC的一个代码生成错误,与指向多维数组的指针有关:隐藏在基于范围的循环中的std::reverse_iterator::operator*()本质上是在执行*--p,其中p是指向数组末尾的int[4]的指针类型。在单个语句中递减和取消引用导致MSVC加载局部变量p的地址,而不是递减的p所指向的前一个元素的地址,这基本上导致返回局部变量p的地址。

您可以在以下独立示例中更好地观察问题(https://godbolt.org/z/x9q5M74Md):

#include <iostream>
using Int4 = int[4]; // To avoid the awkward pointer-to-array syntax
int arr[3][4] = {};
Int4 & test1()
{
Int4 * p = arr;
Int4 * pP1 = p + 1;
// Works correctly
--pP1;
Int4 & deref = *pP1;
return deref;
}
Int4 & test2()
{
Int4 * p = arr;
Int4 * pP1 = p + 1;
// msvc incorrectly stores the address of the local variable pP1 (i.e. &pP1) in deref
Int4 & deref = *--pP1;
return deref;
}

int main()
{
std::cout << "arr   = 0x" << &arr[0][0] << std::endl;
std::cout << "test1 = 0x" << &test1() << std::endl; // Works
std::cout << "test2 = 0x" << &test2() << std::endl; // Bad
}

在本例中,&test1()正确打印arr的第一个元素的地址。但是&test2()实际上打印本地变量test2::pP1的地址,即它打印&test2::pP1。MSVC甚至警告test2()返回局部变量pP1的地址(C4172(。clang和gcc运行良好。MSVC v19.23之前的版本也可以正确编译代码。

查看汇编输出,clang和gcc为test1()test2()发出相同的代码。但微软风投正在做:

; test1()
mov     rax, QWORD PTR pP1$[rsp]
mov     QWORD PTR deref$[rsp], rax
; test2()
lea     rax, QWORD PTR pP1$[rsp]
mov     QWORD PTR deref$[rsp], rax

注意lea而不是mov语句,这意味着test2()加载pP1的地址。

MSVC似乎被指向多维数组的指针弄糊涂了。

相关内容

  • 没有找到相关文章

最新更新