确定 "unknown evaluation order"

being sure about "unknown evaluation order"

从 1.80 版开始,Cppcheck 告诉我

Expression 'msg[ipos++]=checksum(&msg[1],ipos-1)' depends on order of evaluation of side effects

在这个代码序列中(简化,data是一个变量)

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

并将此视为错误,而不是可移植性问题。

它是 C 代码(合并到一个 C++ 主导的项目中),使用 C++98 兼容的编译器编译,同时 运行 几十年来的预期。 Cppcheck 运行 支持 C++03、C89、自动检测语言。

我承认最好重写代码。但在这样做之前,我试图弄清楚:它真的依赖于评估顺序吗?据我了解,首先评估正确的操作数(需要在调用之前评估),然后进行赋值(到 msg[ipos]),最后完成 ipos 的增量。

我这个假设是错误的,还是只是误报?

此代码确实以一种未明确定义的方式依赖于求值顺序:

msg[ipos++] = checksum(&msg[1], ipos-1);

具体来说,没有指定ipos++是在ipos-1计算之前还是之后递增。这是因为 = 处没有“sequence point”,只有在完整表达式(;)的末尾。

函数调用是一个序列点。但这只能保证 ipos-1 在函数调用之前发生。它不保证 ipos++ 之后发生。

看来代码应该这样重写:

msg[ipos] = checksum(&msg[1], ipos-1);
ipos++; // or ++ipos

未指定 = 的操作数的 求值顺序 。因此,首先,代码依赖于未指定的行为。

更糟糕的是 ipos 在同一个表达式中使用了两次,中间没有序列点,用于不相关的目的 - 这会导致未定义的行为。

C99 6.5

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

同样的文本适用于 C90、C99、C++98 和 C++03。在 C11 和 C++11 中,措辞发生了变化,但含义是一样的。这是未定义的行为,C++11 之前的版本。

编译器不需要为未定义的行为提供诊断。你很幸运,它做到了。这不是误报 - 您的代码包含严重错误,从原始 C 代码开始一直如此。