¿Qué hace i = (i, ++ i, 1) + 1; ¿hacer?

Después de leer esta respuesta sobre comportamiento indefinido y puntos de secuencia, escribí un pequeño progtwig:

#include  int main(void) { int i = 5; i = (i, ++i, 1) + 1; printf("%d\n", i); return 0; } 

La salida es 2 . Oh Dios, ¡no vi venir la disminución! ¿Que está sucediendo aquí?

Además, al comstackr el código anterior, recibí una advertencia que decía:

px.c: 5: 8: advertencia: el operando de la izquierda de la expresión de coma no tiene efecto

  [-Wunused-value] i = (i, ++i, 1) + 1; ^ 

¿Por qué? Pero probablemente se responderá automáticamente con la respuesta de mi primera pregunta.

En la expresión (i, ++i, 1) , la coma utilizada es el operador de coma

el operador de coma (representado por el token) es un operador binario que evalúa su primer operando y descarta el resultado, y luego evalúa el segundo operando y devuelve este valor (y escribe).

Debido a que descarta su primer operando, generalmente solo es útil cuando el primer operando tiene efectos secundarios deseables . Si el efecto secundario al primer operando no tiene lugar, entonces el comstackdor puede generar una advertencia sobre la expresión sin efecto.

Entonces, en la expresión anterior, se evaluará la izquierda i y se descartará su valor. Entonces ++i se evaluará y boostá i en 1 y nuevamente se descartará el valor de la expresión ++i , pero el efecto secundario en i es permanente . Entonces 1 será evaluado y el valor de la expresión será 1 .

Es equivalente a

 i; // Evaluate i and discard its value. This has no effect. ++i; // Evaluate i and increment it by 1 and discard the value of expression ++i i = 1 + 1; 

Tenga en cuenta que la expresión anterior es perfectamente válida y no invoca un comportamiento indefinido porque hay un punto de secuencia entre la evaluación de los operandos izquierdo y derecho del operador de coma.

Citando de C11 , capítulo 6.5.17 , operador de coma

El operando izquierdo de un operador de coma se evalúa como una expresión vacía; hay un punto de secuencia entre su evaluación y la del operando correcto. Entonces se evalúa el operando correcto; el resultado tiene su tipo y valor.

Entonces, en tu caso,

 (i, ++i, 1) 

es evaluado como

  1. i , se evalúa como una expresión vacía, valor descartado
  2. ++i , se evalúa como una expresión vacía, el valor se descarta
  3. finalmente, 1 , valor devuelto.

Entonces, la statement final se ve como

 i = 1 + 1; 

y llego a 2 . Supongo que esto responde a ambas preguntas,

  • ¿Cómo obtengo un valor 2?
  • ¿Por qué hay un mensaje de advertencia?

Nota: FWIW, como hay un punto de secuencia presente después de la evaluación del operando de la mano izquierda, una expresión como (i, ++i, 1) no invocará a UB, ya que generalmente uno puede pensar por error.

 i = (i, ++i, 1) + 1; 

Analicémoslo paso a paso.

 (i, // is evaluated but ignored, there are other expressions after comma ++i, // i is updated but the resulting value is ignored too 1) // this value is finally used + 1 // 1 is added to the previous value 1 

Entonces obtenemos 2. Y la tarea final ahora:

 i = 2; 

Lo que sea que estaba antes de que se sobrescriba ahora.

El resultado de

 (i, ++i, 1) 

es

 1 

por

 (i,++i,1) 

la evaluación ocurre de tal manera que el operador descarta el valor evaluado y retendrá el valor más correcto que es 1

Asi que

 i = 1 + 1 = 2 

Encontrará una buena lectura en la página wiki para el operador de Coma .

Básicamente,

… evalúa su primer operando y descarta el resultado, y luego evalúa el segundo operando y devuelve este valor (y escribe).

Esto significa que

 (i, i++, 1) 

a su vez, evaluará i , descartará el resultado, evaluará i++ , descartará el resultado y luego evaluará y devolverá 1 .

Necesitas saber qué está haciendo el operador de coma aquí:

Tu expresión:

 (i, ++i, 1) 

La primera expresión, i , se evalúa, la segunda expresión, ++i , se evalúa y la tercera expresión, 1 , se devuelve para la expresión completa.

Entonces el resultado es: i = 1 + 1 .

Para su pregunta de bonificación, como puede ver, la primera expresión no tiene ningún efecto, por lo que el comstackdor se queja.

La coma tiene una precedencia ‘inversa’. Esto es lo que obtendrá de libros antiguos y manuales en C de IBM (años 70/80). Entonces, el último ‘comando’ es lo que se usa en la expresión principal.

En la C moderna, su uso es extraño, pero es muy interesante en la antigua C (ANSI):

 do { /* bla bla bla, consider conditional flow with several continue's */ } while ( prepAnything(), doSomethingElse(), logic_operation); 

Mientras todas las operaciones (funciones) son llamadas de izquierda a derecha, solo la última expresión se usará como resultado de ‘while’ condicional. Esto evita el manejo de ‘goto’s para mantener un bloque único de comandos para ejecutar antes de la verificación de condición.

EDITAR: Esto evita también una llamada a una función de manejo que podría ocuparse de toda la lógica en los operandos de la izquierda y así devolver el resultado lógico. Recuerde que no teníamos una función en línea en el pasado de C. Entonces, esto podría evitar una sobrecarga de llamadas.