I have a 2D array. It's perfectly okay to iterate the rows in forward order, but when I do it in reverse, it doesn't work. I cannot figure out why.
I'm using MSVC v143 and the C++20 standard.
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");
});
The output for the first for_each
is fine:
0 1 2 3
4 5 6 7
8 9 10 11
Yet the second one is garbage:
-424412040 251 -858993460 -858993460
-424412056 251 -858993460 -858993460
-424412072 251 -858993460 -858993460
When I print their addresses up I couldn't understand it:
<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
What is happening here?
This is very likely a code generation bug of MSVC related to pointers to multidimensional arrays: The
std::reverse_iterator::operator*()
hidden in the range-based loop is essentially doing a*--p
, wherep
is a pointer type to anint[4]
pointing to the end of the array. Decrementing and dereferencing in a single statement causes MSVC to load the address of the local variablep
instead of the address of the previous element pointed to by the decrementedp
, essentially resulting in the address of the local variablep
being returned.You can observe the problem better in the following standalone example (https://godbolt.org/z/x9q5M74Md):
In this example,
&test1()
correctly prints the address of the first element ofarr
. But&test2()
actually prints the address of the local variabletest2::pP1
, i.e. it prints&test2::pP1
. MSVC even warns thattest2()
returns the address of the local variablepP1
(C4172). clang and gcc work fine. Versions before MSVC v19.23 also compile the code correctly.Looking at the assembly output, clang and gcc emit the same code for
test1()
andtest2()
. But MSVC is doing:Notice the
lea
instead of themov
statement, meaning thattest2()
loads the address ofpP1
.MSVC seems to get confused somehow by pointers to multidimensional array.