Puntos de secuencia al llamar a funciones en C y comportamiento indefinido/no especificado

Estoy tratando de precisar mi comprensión de los puntos de secuencia en C, solo quería verificar algo. Actualmente, creo que (1) no está definido mientras que (2) simplemente no está especificado, sobre la base de que en (2), hay puntos de secuencia después de evaluar los argumentos para g y h (así que no estamos modificando i dos veces entre puntos de secuencia), pero el orden de evaluación de los argumentos de f aún no se especifica. ¿Es correcto mi entendimiento?

#include <stdio.h>

int g(int i) {
    return i;
}

int h(int i) {
    return i;
}

void f(int x, int y) {
    printf("%i", x + y);
}

int main() {
    int i = 23;
    f(++i, ++i); // (1)
    f(g(++i), h(++i)); // (2)
    return 0;
}

EDIT:

Parece que el punto clave aquí es si el compilador es libre de realizar ambos incrementos antes de g or h se llama: mi entendimiento de las respuestas a continuación es que lo es, aunque agradecería la confirmación de que ese es el caso.

preguntado el 12 de junio de 12 a las 16:06

@MichaelDorgan: No lo haré :) Estoy trabajando en una herramienta de análisis estático para ayudar a evitar que la gente haga este tipo de cosas y la distinción podría ser importante. -

2 Respuestas

Incorrecto. Los puntos de secuencia especifican un Orden parcial sobre el orden permitido de las operaciones. En el caso (2), hay puntos de secuencia:

  1. En el punto y coma al final de la línea (1)
  2. Después de la evaluación de los argumentos de g (es decir, la ++i) pero antes de la llamada a g
  3. Después de la evaluación de los argumentos de h (es decir, la ++i) pero antes de la llamada a h
  4. Después de la evaluación de los argumentos de f (es decir, después f y g han regresado) pero antes de la llamada a f
  5. Después del regreso de f

Entonces, el orden parcial se ve así, de arriba a abajo:

    1
   / \
  /   \
 2     3
  \   /
   \ /
    4
    |
    | 
    5

2 y 3 no están ordenados entre sí, ya que no se especifica el orden de evaluación de los argumentos. Ya que i se modifica dos veces entre los puntos de secuencia 1 y 4, el comportamiento no está definido.

Respondido el 12 de junio de 12 a las 16:06

Ah cierto, ¡me alegro de haberlo comprobado! Era consciente del hecho de que los puntos de secuencia son un orden parcial, pero estaba pensando de la siguiente manera: dado que el orden de evaluación del argumento no está especificado, obtienes 1 -> 2 -> 3 -> 4 -> 5 o 1 -> 3 -> 2 -> 4 -> 5. En cualquier caso, pensé que había un punto de secuencia entre 1 y 4 (en 2 o 3). Parece que me perdí el punto por completo. - Estuardo Golodetz

No estoy en desacuerdo con este enfoque, pero creo que no se deriva inmediatamente del estándar; 6.5 2 dice que un objeto puede modificarse como máximo una vez entre adyacente puntos de secuencia, pero 1 y 4 no son adyacentes en el gráfico anterior. ¿Se requiere que la implementación se comporte como si el gráfico de puntos de secuencia estuviera linealizado? - ecatmur

@ecatmur: Hmm, buen punto. Revisé el estándar y tampoco puedo entenderlo. C99 §6.5/2 dice "Entre el punto de secuencia anterior y el siguiente...", pero no está claro cuál es el "punto de secuencia siguiente" después del n.° 1: el orden de evaluación de g y h no está especificado, y cada uno tiene un punto de secuencia después de evaluar sus argumentos pero antes de llamarlos, y esos dos puntos de secuencia ocurren antes del #4. - adam rosenfield

@AdamRosenfield: Ese es el quid de mi malentendido sobre esto: si fuera definitivamente el caso de que todo g(++i) se evaluó primero, o la totalidad de h(++i) era, entonces habría un punto de secuencia entre los dos incrementos en cualquier caso y el comportamiento aparentemente no estaría indefinido (aunque tal vez lo estaría en un sentido formal). Pero si los incrementos pueden ocurrir antes de llamar a cualquiera g or h, entonces el comportamiento sería evidentemente indefinido en lugar de no especificado. Mi entendimiento actual es que ese es el punto aquí. - Estuardo Golodetz

@Stuart: aunque el estándar no dice esto explícitamente, me pregunto si podría interpretarse en el sentido de "Si hay un orden permitido de efectos secundarios y puntos de secuencia que daría lugar a que un objeto se modifique más de una vez entre los puntos de secuencia, el comportamiento es indefinido"? Porque con esa redacción, el comportamiento claramente no estaría definido, ya que el compilador podría poner ambos ++i's antes de llamar a cualquiera g or h (aunque no es obligatorio hacerlo). - adam rosenfield

No, según 6.5.2.2 10, no hay un punto de secuencia entre la evaluación de los argumentos de la subexpresión, justo antes de la llamada real.

Una forma de verlo es que no está especificado. si el comportamiento es indefinido; si la implementación secuencia los dos ++i subexpresiones antes de cualquier llamada a g or h entonces el comportamiento es indefinido, pero si el ++i subexpresiones se evalúan lo más tarde posible (inmediatamente antes de llamar g y h respectivamente), entonces el comportamiento no está especificado. Sin embargo, debido a que la implementación siempre tiene la libertad de elegir entre cualquier comportamiento no especificado permitido, el resultado general no está definido.

Respondido el 13 de junio de 12 a las 09:06

Correcto, entonces es posible para los dos ++is a ambos suceder antes g y h son llamados, en cuyo caso no hay ningún punto de secuencia en el camino y sería indefinido. - Estuardo Golodetz

no especificado si el comportamiento no está definido: Creo que no existe tal cosa en C. El comportamiento no está definido aquí. - Guau

Creo que a pesar de la terminología, voy a dar esta respuesta, ya que en realidad me ayudó a pensar más sobre esto (aunque voté ambas respuestas porque ambas fueron útiles). Probablemente sería útil para los futuros lectores si la terminología pudiera ajustarse, aunque supongo. - Estuardo Golodetz

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.