Since version 1.80, Cppcheck tells me that
Expression 'msg[ipos++]=checksum(&msg[1],ipos-1)' depends on order of evaluation of side effects
in this code sequence (simplified, data
is a variable)
BYTE msg[MAX_MSG_SIZE]; // msg can be smaller, depending on data encoded int ipos = 0; msg[ipos++] = MSG_START; ipos += encode(&msg[ipos], data); msg[ipos++] = checksum(&msg[1], ipos-1); // <---- Undefined Behaviour? msg[ipos++] = MSG_END; // increment ipos to the actual size of msg
and treats this as an error, not a portability issue.
It's C code (incorporated into a C++-dominated project), compiled with a C++98-complient compiler, and meanwhile runs as expected for decades. Cppcheck is run with C++03, C89, auto-detect language.
I confess that the code should better be rewritten. But before doing this, I try to figure out: Is it really dependent on evaluation order? As I understand it, the right operand is being evaluated first (it needs to before the call), then the assignment is taking place (to msg[ipos]
) with the increment of ipos
done last.
Am I wrong with this assumption, or is it just a false positive?
Do not depend on the order of evaluation for side effects unless there is an intervening sequence point. If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
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. Whether the left operand is evaluated first or the right operand is evaluated first is not specified by the language.
The function is called first, the sequence point occurs, it modifies the pointer ppm by pointing it to new allocated struct, followed by a sequence point. Then it stores the value 1000 to the member m , followed by a sequence point.
This code does indeed depend on evaluation order in a way which is not well defined:
msg[ipos++] = checksum(&msg[1], ipos-1);
Specifically, it is not specified whether ipos++
will increment before or after ipos-1
is evaluated. This is because there is no "sequence point" at the =
, only at the end of the full expression (the ;
).
The function call is a sequence point. But that only guarantees that ipos-1
happens before the function call. It does not guarantee that ipos++
happens after.
It appears the code should be rewritten this way:
msg[ipos] = checksum(&msg[1], ipos-1); ipos++; // or ++ipos
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With