c++ - how - cppcheck inconclusive
estar seguro acerca de "orden de evaluaciĆ³n desconocido" (2)
Desde la versión 1.80, Cppcheck me dice que
La expresión ''msg [ipos ++] = checksum (& msg [1], ipos-1)'' depende del orden de evaluación de los efectos secundarios
en esta secuencia de código (simplificado, los data
son una 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
y trata esto como un error, no como un problema de portabilidad.
Es un código C (incorporado en un proyecto dominado por C ++), compilado con un compilador compatible con C ++ 98 y, mientras tanto, se ejecuta como se espera durante décadas. Cppcheck se ejecuta con C ++ 03, C89, idioma de detección automática.
Confieso que el código debería ser reescrito. Pero antes de hacer esto, trato de averiguar: ¿Depende realmente del orden de evaluación? Como lo entiendo, el operando correcto se está evaluando primero (es necesario antes de la llamada), luego se está realizando la asignación (a msg[ipos]
) con el incremento de ipos
finalizado.
¿Me equivoco con esta suposición, o es solo un falso positivo?
El orden de evaluación de los operandos de =
no está especificado. Entonces, para empezar, el código se basa en un comportamiento no especificado.
Lo que hace que el caso sea aún peor es que ipos
se usa dos veces en la misma expresión sin un punto de secuencia en el medio, para fines no relacionados, lo que conduce a un comportamiento indefinido.
C99 6.5
Entre el punto de secuencia anterior y el siguiente, un objeto tendrá su valor almacenado modificado a lo sumo una vez por la evaluación de una expresión. Además, el valor anterior se leerá solo para determinar el valor que se almacenará.
El mismo texto se aplica a C90, C99, C ++ 98 y C ++ 03. En C11 y C ++ 11 la redacción ha cambiado pero el significado es el mismo. Es un comportamiento indefinido, pre-C ++ 11.
No se requiere que el compilador dé un diagnóstico para un comportamiento indefinido. Tuviste suerte de que lo hiciera. No es un falso positivo: su código contiene un error grave, desde el código C original.
Este código realmente depende del orden de evaluación de una manera que no está bien definida:
msg[ipos++] = checksum(&msg[1], ipos-1);
Específicamente, no se especifica si ipos++
aumentará antes o después ipos-1
evaluar ipos-1
. Esto se debe a que no hay un " punto de secuencia " en =
, solo al final de la expresión completa (the ;
).
La llamada a la función es un punto de secuencia. Pero eso solo garantiza que ipos-1
ocurra antes de la llamada a la función. No garantiza que ipos++
ocurra después.
Parece que el código debería reescribirse de esta manera:
msg[ipos] = checksum(&msg[1], ipos-1);
ipos++; // or ++ipos