2010-07-24 13 views
9

Tengo el siguiente códigoComportamiento de una expresión: ¿definido o indefinido?

int m[4]={1,2,3,4}, *y; 
y=m; 
*y = f(y++); // Expression A 

Mi amigo me dijo que Expression A tiene un comportamiento bien definido pero no estoy seguro de si es correcta.

Según él la función f() introduce un sequence point en el medio y, por lo tanto, el comportamiento está bien definido.

Alguien aclare por favor.

P.S: Sé que no deberíamos escribir dicho código para fines prácticos. Es solo con el propósito de aprender. :)

+3

Yo digo que no. En eso es lo que 'gcc' me dice. – kennytm

Respuesta

15

En el mejor de los casos, el código en cuestión tiene un comportamiento no especificado. Para los operadores de asignación, "el orden de evaluación de los operandos no está especificado" (C99 §6.5.16/4).

Si el operando de la izquierda se evalúa primero, el resultado de f(y++) se almacenará en m[0]. Si el operando correcto se evalúa primero, el resultado se almacenará en m[1].

En cuanto a si el comportamiento es indefinido, el párrafo pertinente es:

Entre el anterior y el siguiente punto de la secuencia de un objeto tendrá su valor almacenado modi fi ed como máximo 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á (C99 §6.5/2).

Si el lado izquierdo se evalúa primero, luego ir en contra de la segunda frase, porque el orden es:

  1. El valor de y se lee en el lado izquierdo para eliminar la referencia
  2. El el valor de y se lee en el lado derecho para incrementarlo
  3. Hay un punto de secuencia después de la evaluación de los argumentos de la función (por lo tanto, se completa el efecto secundario de y++ y se escribe y)

En el paso 1, se lee el "valor anterior" de y pero con un propósito que no sea "determinar el valor que se almacenará". Por lo tanto, el comportamiento no está definido porque una orden de evaluación válida produce un comportamiento indefinido.

+2

+1. Creo que incluso está indefinido, porque las evaluaciones de '* y' y' y ++ 'no se han secuenciado y la segunda induce un efecto secundario sobre' y'. – avakar

+0

Gracias James, tu respuesta está perfectamente bien. AC :-) –

0

EDITAR: esto es incorrecto, sin embargo lo dejo aquí porque la discusión que sigue en los comentarios es algo esclarecedora, y espero valiosa.

Está bien definido en función del orden de evaluación de los operadores en C (o C++).

Evaluación de fuerzas de asignación del lado derecho de la expresión primero. La aplicación de la función obliga a la evaluación de sus argumentos primero, por lo que el efecto parece razonablemente claro (aunque no he intentado ejecutarlo, ¡así que no dude en corregirme!). Podemos reescribir esto utilizando variables temporales (yo los llamaré T0 y T1), y creo que esto podría ser un poco más claro:

t0 = y++; 
t1 = f(t0); 
*y = t1; 

El término "punto de secuencia" es un poco de una pista falsa. Un punto de secuencia realmente no se crea, sino que es solo la consecuencia de tener un orden de evaluación estricto definido para el idioma.

EDIT: Si bien esta respuesta parece intelectualmente satisfactoria, la respuesta de James McNellis cita a la pieza correspondiente de la especificación C99 que indica que el orden de evaluación de la asignación es no bien definido. Todo el crédito para él por verificar sus hechos. Voy a revisar mi respuesta de "está bien definida" a "probablemente esté bien definida con respecto a un compilador en particular", ya que creo que es poco probable que la mayoría de los compiladores cambien regularmente el orden en que emiten dicho código (Digo "probablemente" para explicar cualquier optimización muy agresiva).

+2

_Assignment forces evaluation del lado derecho de la expresión first._ Esto no es cierto: el orden de evaluación no está especificado. –

+1

¿Dónde indica que el lado derecho de una tarea debe evaluarse primero? – jalf

+0

Gracias, acabo de editar mi respuesta a la cuenta para ello, y gracias por enseñarme algo nuevo sobre C :) – Gian

1

La expresión no está bien definida:

una interpretación válida de la expresión es:

(1) int* t0 = y++; 
(2) int t1 = f(t0); 
(3) int& t2 = *y; 
----------------- 
t2 = t1; 

Una interpretación igualmente válida de la expresión es:

(1) int& t2 = *y; 
(2) int* t0 = y++; 
(3) int t1 = f(t0); 
----------------- 
t2 = t1; 

Ambos son vaalid y generar diferentes resultados. Entonces la expresión tiene un resultado indefinido.

+1

En general, el hecho de que una expresión pueda tener dos resultados diferentes simplemente significa que el resultado es * no especificado *. Sin embargo, en este caso particular, el resultado es, en efecto sin definir como los cálculos de 't0' y' t2' puede ocurrir en cualquier orden y 't0' tiene efectos secundarios en' y' mientras que 't2' utiliza el valor de' y'. – avakar

13

Tiene toda la razón al llamar a la función introduciendo un punto de secuencia. Sin embargo, ese punto de secuencia no guarda la situación en su caso.

consideran este primer ejemplo sencillo

i = some_function(i++); 

¿Es válido? Sí lo es. ¿Por qué? Es válido porque el punto de secuencia introducido por la función (del que está hablando) separa dos modificaciones de i entre sí, lo que hace que el código sea válido. No hay un orden de evaluación de esta expresión que daría como resultado que i se modificara dos veces sin un punto de secuencia intermedio.

Sin embargo, vamos a volver a su variante

*y = f(y++); 

En este caso existe el punto de que la secuencia también. Sin embargo, el lenguaje no garantiza el orden de evaluación del operador = (es decir, el lenguaje no garantiza qué operador de asignación de asignación se evalúa primero: izquierda o derecha). Es bastante permisible para el compilador evaluar primero el lado izquierdo (*y), el segundo argumento de función (y++), luego llamar a la función y luego realizar la asignación real. En este escenario potencial, los primeros dos pasos, leer el y y modificar el y, no están separados por un punto de secuencia. Por lo tanto, el comportamiento no está definido.

+1

@Ben Voigt: I estaba hablando sobre el orden relativo de la evaluación de los operandos de '' = operador, es decir, si el LHS o RHS es evaluado primero. – AnT

Cuestiones relacionadas