2011-05-19 17 views
21

Pregunta relacionada: Any good reason why assignment operator isn't a sequence point?es i = f(); definido cuando f modifica i?

Del comp.lang.c FAQ inferiría que el programa siguiente no está definido. Curiosamente, solo menciona la llamada a f como un punto de secuencia, entre el cálculo de los argumentos y la transferencia de control a f. La transferencia de control de f a la expresión de llamada no aparece como un punto de secuencia.

int f(void) { i++; return 42; } 
i = f(); 

¿Es realmente indefinido?

Como nota final que agrego a muchas de mis preguntas, estoy interesado en esto en el contexto del análisis estático. No estoy escribiendo esto solo, solo quiero saber si debería advertirlo en programas escritos por otros.

+19

Para quien corresponda, "Siempre puedes intentarlo" nunca es una respuesta correcta a "¿Es X un comportamiento indefinido?". –

+0

+1, pero también tenga en cuenta que UB no es necesariamente malo. C no prohíbe el uso de UB, simplemente dice "No sé lo que sucederá. Espero que lo hagas". – Philip

+5

UB es malo. A diferencia del comportamiento definido por la implementación, el uso previsto para los autores del compilador es que el comportamiento puede cambiar y cambiará incluso entre diferentes versiones del mismo compilador, de acuerdo con lo que mejor sirva para la optimización del código válido. –

Respuesta

9

La transferencia de control de f de nuevo a la expresión que llama no aparece en la lista como punto de secuencia.

Sí, lo es.

al final de la evaluación de una expresión completa

 

La expresión completa que forma una declaración expresión, o uno de los expresiones controladores de una if, cambiar, while, for o do/while instrucción, o la expresión en un inicializador o una declaración de devolución.

Tiene una declaración de devolución, por lo tanto, tiene un punto de secuencia.

ni siquiera parece que

int f(void) { return i++; } // sequence point here, so I guess we're good 
i = f(); 

no está definido. (Lo cual para mí es un poco raro.)

+2

Entonces, ¿qué sucede si el compilador intenta alinear esto? Editar: Todo se rompe, es bueno saber ... –

+0

Oh, veo por qué, (return i ++) se evalúa como i esperado y luego aumenta i, punto de final de secuencia. Luego asigne el viejo i al i, punto final de secuencia. –

+10

@James Greenhalgh: Nada se rompe. Inlinear no cambia absolutamente nada con respecto a los puntos de secuencia. El "punto de secuencia" es un concepto, no una entidad física asociada de algún modo con un cuerpo de función física. El código está bien en cualquier caso, ya que cada función tiene un punto de secuencia en la entrada y al final, independientemente de si está en línea o no. – AnT

8

Eso no está indefinido en absoluto. Uno de los puntos de secuencia enumerados en el Apéndice C de C99 es el final de una expresión completa, de la que uno es la expresión en una declaración de devolución.

Dado que regresa 42, hay un punto de secuencia inmediatamente después de esa declaración return.

Para completar, los puntos de secuencia C99 se enumeran aquí, con el correspondiente uno en negrita:

Los siguientes son los puntos de secuencia descrita en 5.1.2.3:


  • la llamada a una función, después de que los argumentos hayan sido evaluados (6.5.2.2).
  • Fin del primer operando de los siguientes operadores: lógico AND & & (6.5.13); O lógico || (6.5.14); condicional? (6.5.15); coma, (6.5.17).
  • El final de un declarador completo: declaradores (6.7.5);
  • El final de una expresión completa: un inicializador (6.7.8); la expresión en una declaración de expresión (6.8.3); la expresión de control de una declaración de selección (si o cambio) (6.8.4); la expresión de control de una sentencia while o do (6.8.5); cada una de las expresiones de una declaración for (6.8.5.3); la expresión en una declaración de devolución (6.8.6.4).
  • Inmediatamente antes de que una función de biblioteca regrese (7.1.4).
  • Después de las acciones asociadas con cada especificador de conversión de función de entrada/salida formateada especificador (7.19.6, 7.24.2).
  • Inmediatamente antes e inmediatamente después de cada llamada a una función de comparación, y también entre cualquier llamada a una función de comparación y cualquier movimiento de los objetos pasados ​​como argumentos para esa llamada (7.20.5).
+0

¿Estás seguro de que tu ejemplo es UB sin la función? Creo que está perfectamente bien definido. El objeto es accedido (leído) solo una vez, y tiene el propósito de determinar el nuevo valor que se le asignará. No veo nada que invoque a UB. –

+0

Ah, veo que es del estándar, pero todavía no puedo encontrar ninguna justificación de por qué sería UB ... –

+1

@R. ¿Es porque g es una unión? g.u2.f3 = g.u1.f2 ciertamente parece que invocaría behvaiour indefinido. –

Cuestiones relacionadas