确定"未知的评估顺序"

时间:2017-08-29 11:22:51

标签: c++ c static-analysis operator-precedence cppcheck

自1.80版以来,Cppcheck告诉我

  

表达式' msg [ipos ++] =校验和(& msg [1],ipos-1)'取决于副作用评估的顺序

在此代码序列中(简化,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)。

这个假设我错了,还是只是假阳性?

2 个答案:

答案 0 :(得分:41)

此代码确实依赖于评估顺序,其方式尚未明确定义:

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

答案 1 :(得分:8)

=的操作数的评估顺序未指定。首先,代码依赖于未指定的行为。

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

C99 6.5

  

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。此外,先前的值应该是只读的,以确定要存储的值。

同一文本适用于C90,C99,C ++ 98和C ++ 03。在C11和C ++ 11中,措辞已经改变,但含义是相同的。这是未定义的行为,前C ++ 11。

编译器不需要对未定义的行为进行诊断。你很幸运。这不是误报 - 您的代码包含一个严重的错误,一直到原始的C代码。