Reputation: 311146
I have some obfuscated code specially written to test the subscript operator.
From my point of view there is used a correct subscripting expression according to the C++ Standard (the so-called sub-expressions E1 and E2 are sequenced. At least in (starting from) the C++17 Standard there is explicitly written: "The expression E1 is sequenced before the expression E2.") . But some compilers as for example MS VS-22 C++ (with supporting the latest C++ Standard) or gcc 8.3 issue a run-time error.
#include <iostream>
int main()
{
int a[] = { 1, 2, 3, 4, 5 };
for (const int *p = a; p != a + sizeof( a ) / sizeof( *a ); ++p)
{
std::cout << ( *p++ - 1 )[( --p, a )] << ' ';
}
std::cout << '\n';
}
or
import std;
int main()
{
int a[] = { 1, 2, 3, 4, 5 };
for (const int *p = a; p != a + std::size( a ); ++p)
{
std::cout << ( *p++ - 1 )[( --p, a )] << ' ';
}
std::cout << '\n';
}
Is it a bug of the compilers or do I have missed something important in the C++ Standard relative to subscripting expressions?
Upvotes: 1
Views: 92
Reputation: 311146
It is a bug of MS VS compilers.
Starting from the C++17 Standard there were introduced sequences of evaluations of sub-expressions in some expressions with for example shift operators and the same subscipt operator.
From the C++17 Standard (8.2.1 Subscripting):
1 A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall be a glvalue of type “array of T” or a prvalue of type “pointer to T” and the other shall be a prvalue of unscoped enumeration or integral type. The result is of type “T”. The type “T” shall be a completely-defined object type.66 The expression E1[E2] is identical (by definition) to *((E1)+(E2)) [ Note: see 8.3 and 8.7 for details of * and + and 11.3.4 for details of arrays. — end note ] , except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise. The expression E1 is sequenced before the expression E2.
However it seems that Microsoft did not make corresponding changes for its compilers relative to the subscript operator.
The subscripting expression in the presented program is evaluated by the MS VS22 C++ compiler starting from the sub-expression in square braces. As result the decremented pointer p
in the first iteration of the loop is used to access memory outside the array that invokes undefined behavior.
To prove that it is enough to change the for loop the following way
for (const int *p = a + 1; p != a + std::size( a ); ++p)
though the last element of the array in this case will not be outputted.
Upvotes: 1