确定“未知的评估顺序”

从版本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,自动检测语言运行。

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

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

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

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

具体来说,没有指定ipos++是否会在评估ipos-1之前或之后递增。 这是因为=处没有“ 序列点 ”,只在完整表达式的末尾( ; )。

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

似乎应该以这种方式重写代码:

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

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

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

C99 6.5

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

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

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