D:\code\cplusplus231847>g++ main.cpp -Wpedantic -Wall main.cpp: In function 'int main()': main.cpp:11:9: warning: operation on 'ptr' may be undefined [-Wsequence-point] *ptr++ = tolower(*ptr); ~~~^~ |
|
|
dEF |
*(ptr++) = *(ptr);
(redundant parenthesis for emphasis)ptr++ = ptr;
clang++ -Wall -std=c++14 -stdlib=libc++ -O2 -o a.out source_file.cpp |
source_file.cpp:11:9: warning: unsequenced modification and access to 'ptr' [-Wunsequenced] *ptr++ = tolower(*ptr); ^ ~~~ 1 warning generated. dEF |
If we remove the tolower, isn't this basically the same as doing: *(ptr++) = *(ptr); (redundant parenthesis for emphasis) I don't understand how that's more defined than ptr++ = ptr; |
|
|
|
|
x[0] = x[1];
x[0] = x[0];
x[i++] = x[i];
x[i] = x[i+1];
(left-hand-side i++ evaluated first)x[i] = x[i];
(right-hand side i evaluated first)The side effect (modification of the left argument) of the built-in assignment operator [...] is sequenced after the value computation (but not the side effects) of both left and right arguments, ... |
If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent, the behavior is undefined. |
i + i++
is undefined (and so would OP's expression), but oddly enough the examples following this paragraph only use double side effects on the same scalar:
|
|
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; their result is an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation. http://eel.is/c++draft/expr.ass |
i = v[i++]
i
in the same expression, whereas:*ptr++ = f(*ptr)
ptr
: the post-increment. The other side effect modifies the object referenced by the pointer, and this modification:ptr
|
|
x[0]++ = x[0]; // Case 2: undefined
*ptr++ = f(*ptr)
*ptr = f(*(ptr+1))
*ptr = f(*ptr)
The other side effect modifies the object referenced by the pointer, and this modification: • pre-C++17: is sequenced before the post-evaluation modification of ptr |
-Wsequence-point ... The C++17 standard will define the order of evaluation of operands in more cases: in particular it requires that the right-hand side of an assignment be evaluated before the left-hand side, so the above examples are no longer undefined. But this warning will still warn about them, to help people avoid writing code that is undefined in C and earlier revisions of C++. |
|
|
> alias c++ clang++-devel -std=c++17 -stdlib=libc++ -Wall -Wextra -pedantic-errors -g > c++ --version | grep clang && c++ -c main.cpp clang version 7.0.0 main.cpp:8:9: warning: unsequenced modification and access to 'ptr' [-Wunsequenced] *ptr++ = std::tolower(*ptr) ; ^ ~~~ 1 warning generated. > > alias g++ g++8 -std=c++17 -Wall -Wextra -pedantic-errors -g -Wl,-rpath=/usr/local/lib/gcc8 > g++ --version | grep g++ && g++ -c main.cpp g++8 (FreeBSD Ports Collection) 8.0.1 20180211 (experimental) main.cpp: In function 'int main()': main.cpp:8:9: warning: operation on 'ptr' may be undefined [-Wsequence-point] *ptr++ = std::tolower(*ptr) ; ~~~^~ |
FWIW: Even in C++17, both g++ and clang++ continue to generate undefined behaviour warnings for constructs which are no longer undefined in C++17. |
<cctype>
:
|
|