¿Cómo se evalúan los argumentos en una llamada de función?

Considere este código:

void res(int a,int n) { printf("%d %d, ",a,n); } void main(void) { int i; for(i=0;i<5;i++) res(i++,i); //prints 0 1, 2 3, 4 5 for(i=0;i<5;i++) res(i,i++); //prints 1 0, 3 2, 5 4 } 

En cuanto a la salida, parece que los argumentos no se evalúan de derecha a izquierda cada vez. ¿Qué está pasando exactamente aquí?

El orden de evaluación de los argumentos en una llamada de función no está especificado. El comstackdor puede evaluarlos en cualquier orden que decida.

Del estándar C99 6.5.2.2/10 “Llamadas de funciones / semántica”:

El orden de evaluación del designador de la función, los argumentos reales y las subexpresiones dentro de los argumentos reales no está especificado, pero hay un punto de secuencia antes de la llamada real.

Si necesita asegurarse un pedido en particular, usar temporales es la solución habitual:

 int i; for(i=0;i<5;i++) { int tmp = i; int tmp2 = i++; res(tmp2,tmp); } 

Aún más importante (ya que da como resultado un comportamiento indefinido, no solo un comportamiento no especificado) es que generalmente no puede usar un operando para los operadores de incremento / decremento más de una vez en una expresión. Eso es porque:

Entre el punto de secuencia anterior y el siguiente, un objeto tendrá su valor almacenado modificado a lo sumo 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á. (6.5 / 2 "Expresiones")

Según la norma: el orden de evaluación de los argumentos no está especificado. Además, tenga en cuenta que no hay puntos de secuencia (tipo de publicaciones de millas) cuando se evalúan los argumentos. Por lo tanto, la modificación de la misma variable como parte del argumento, más de una vez evoca un comportamiento indefinido. Esta es una FAQ 3.2 . El código que has publicado tiene un comportamiento ambiguo.

Puede sorprender por qué el estándar deja esto sin especificar: la razón simple es que esto permite que el comstackdor realice algunas optimizaciones. (Vea la discusión relacionada con la Q2 de GOTW # 56 ).

Sin embargo, en la mayoría de las implementaciones, esto está determinado por lo que se conoce como la convención de llamada. La convención de llamada no solo determina el orden, sino que también impone la responsabilidad de limpiar la stack de la persona que llama o de la persona que llama.

También tenga en cuenta, main siempre devuelve un int .