tipos programacion operadores logicos expresiones definicion aritmeticos c++ c++11 undefined-behavior language-lawyer unspecified-behavior

c++ - logicos - operadores y expresiones en programacion



¿Es un comportamiento indefinido si múltiples operandos en una expresión compuesta modifican el mismo objeto? (3)

Recuerdo vagamente haber leído en alguna parte que es un comportamiento indefinido si varios operandos en una expresión compuesta modifican el mismo objeto.

Creo que un ejemplo de esta UB se muestra en el código a continuación, sin embargo, he compilado en g ++, clang ++ y visual studio y todos imprimen los mismos valores y parece que no pueden producir valores impredecibles en diferentes compiladores.

#include <iostream> int a( int& lhs ) { lhs -= 4; return lhs; } int b( int& lhs ) { lhs *= 7; return lhs; } int c( int& lhs ) { lhs += 1; return lhs; } int d( int& lhs ) { lhs += 2; return lhs; } int e( int& lhs ) { lhs *= 3; return lhs; } int main( int argc, char **argv ) { int i = 100; int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i ); std::cout << i << ", " << j << std::endl; return 0; }

¿Es este comportamiento indefinido o he evocado de alguna manera una descripción de la supuesta UB que no es realmente indefinida?

Estaría agradecido si alguien pudiera publicar un ejemplo de esta UB y tal vez me indique dónde está en la norma C ++ que dice que es UB.


No es un comportamiento indefinido pero tiene resultados no especificados: el único objeto modificado es i través de las referencias pasadas a las funciones. Sin embargo, la llamada a las funciones introduce puntos de secuencia (no tengo el C ++ 2011 conmigo: allí se llaman algo diferente), es decir, no hay problema de múltiples cambios dentro de una expresión que causan un comportamiento indefinido.

Sin embargo, el orden en que se evalúa la expresión no se especifica. Como resultado, puede obtener resultados diferentes si cambia el orden de la evaluación. Este no es un comportamiento indefinido: el resultado es uno de todos los posibles órdenes de evaluación. Un comportamiento indefinido significa que el programa puede comportarse de la manera que desee, incluida la producción de los resultados "esperados" (esperados por el programador) para la expresión en cuestión mientras se eliminan todos los demás datos.


No, no es un comportamiento indefinido.

Pero sí invoca un comportamiento no especificado .

Esto se debe a que el orden en que se evalúan las subexpresiones no está especificado.

int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );

En la expresión anterior las sub expresiones:

b(i) c(i) e(i) a(i) d(i)

Se puede evaluar en cualquier orden. Debido a que todos tienen efectos secundarios, los resultados dependerán de este orden.

Si divide la expresión en todas las subexpresiones (esto es pseudo-código)
Entonces puedes ver cualquier pedido requerido. Las expresiones anteriores no solo se pueden hacer en ningún orden, sino que también pueden intercalarse con las subexpresiones de nivel superior (con solo unas pocas restricciones).

tmp_1 = b(i) // A tmp_2 = c(i) // B tmp_3 = e(i) // C tmp_4 = a(i) // D tmp_5 = d(i) // E tmp_6 = tmp_1 + tmp_2 // F (Happens after A and B) tmp_7 = tmp_6 * tmp_3 // G (Happens after C and F) tmp_8 = tmp_7 / tmp_4 // H (Happens after D and G) tmp_9 = tmp_8 * tmp_5 // I (Happens after E and H) int j = tmp_9; // J (Happens after I)


No, no es. El comportamiento indefinido está fuera de discusión aquí (asumiendo que la aritmética int no se desborda): todas las modificaciones de i están aisladas por puntos de secuencia (usando la terminología C ++ 03). Hay un punto de secuencia en la entrada de cada función y hay un punto de secuencia en la salida.

El comportamiento no está especificado aquí.

Su código en realidad sigue el mismo patrón que el ejemplo clásico que se usa a menudo para ilustrar la diferencia entre un comportamiento indefinido y no especificado . Considera esto

int i = 1; int j = ++i * ++i;

La gente a menudo afirmará que en este ejemplo el "resultado no depende del orden de evaluación y, por lo tanto, j siempre debe ser 6". Esta es una reclamación no válida, ya que el comportamiento no está definido.

Sin embargo en este ejemplo

int inc(int &i) { return ++i; } int i = 1; int j = inc(i) * inc(i);

el comportamiento es formalmente sólo no especificado. A saber, el orden de evaluación no está especificado. Sin embargo, dado que el resultado de la expresión no depende en absoluto del orden de evaluación, se garantiza que j siempre terminará como 6 . Este es un ejemplo de cómo una combinación de comportamiento no especificado generalmente peligrosa puede llevar a resultados perfectamente definidos.

En su caso, el resultado de su expresión depende críticamente del orden de evaluación, lo que significa que el resultado será impredecible. Sin embargo, aquí no hay un comportamiento indefinido, es decir, el programa no tiene permiso para formatear su disco duro. Solo se permite producir resultados impredecibles en j .

De nuevo, podría resultar que algunos de los escenarios de evaluación de su expresión conduzcan a un desbordamiento de enteros con signo (no los he analizado todos), lo que por sí solo provoca un comportamiento indefinido. Por lo tanto, todavía hay un potencial de comportamiento no especificado que conduce a un comportamiento indefinido en su expresión. Pero esto probablemente no es de lo que trata tu pregunta.