Orden de evaluación de parámetros antes de una función invocando C

¿Se puede suponer un orden de evaluación de los parámetros de la función al llamarlo en C? De acuerdo con el siguiente progtwig, parece que no hay un orden en particular cuando lo ejecuté.

#include  int main() { int a[] = {1, 2, 3}; int * pa; pa = &a[0]; printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa)); /* Result: a[0] = 3 a[1] = 2 a[2] = 2 */ pa = &a[0]; printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa)); /* Result: a[0] = 2 a[1] = 2 a[2] = 2 */ pa = &a[0]; printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa)); /* a[0] = 2 a[1] = 2 a[2] = 1 */ } 

No, los parámetros de la función no se evalúan en un orden definido en C.

Vea las respuestas de Martin York a ¿Cuál es el comportamiento indefinido común que el progtwigdor de C ++ debe conocer? .

El orden de evaluación de los argumentos de la función no está especificado, de C99 §6.5.2.2p10:

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

La fraseología similar existe en C89.

Además, está modificando pa varias veces sin intervenir puntos de secuencia que invoca un comportamiento indefinido (el operador de coma introduce un punto de secuencia pero las comas que delimitan los argumentos de función no). Si aparece las advertencias en su comstackdor, debería advertirle sobre esto:

 $ gcc -Wall -W -ansi -pedantic test.c -o test test.c: In function 'main': test.c:9: warning: operation on 'pa' may be undefined test.c:9: warning: operation on 'pa' may be undefined test.c:13: warning: operation on 'pa' may be undefined test.c:13: warning: operation on 'pa' may be undefined test.c:17: warning: operation on 'pa' may be undefined test.c:17: warning: operation on 'pa' may be undefined test.c:20: warning: control reaches end of non-void function 

Solo para agregar algunas experiencias.
El siguiente código:

 int i=1; printf("%d %d %d\n", i++, i++, i); 

resultados en

2 1 3 – usando g ++ 4.2.1 en Linux.i686
1 2 3 – usando SunStudio C ++ 5.9 en Linux.i686
2 1 3 – usando g ++ 4.2.1 en SunOS.x86pc
1 2 3 – usando SunStudio C ++ 5.9 en SunOS.x86pc
1 2 3 – usando g ++ 4.2.1 en SunOS.sun4u
1 2 3 – usando SunStudio C ++ 5.9 en SunOS.sun4u

¿Se puede suponer un orden de evaluación de los parámetros de la función al llamarlo en C?

No, no puede suponerse que, si se trata de un comportamiento no especificado , el borrador del estándar C99 en la sección 6.5 párrafo 3 dice:

La agrupación de operadores y operandos se indica con la syntax.74) Excepto como se especifica más adelante (para los operadores de función-llamada (), &&, ||,?: Y coma), el orden de evaluación de las subexpresiones y el orden en qué efectos secundarios tienen lugar no están especificados.

También dice que excepto como se especifica más adelante y específicamente los sitios function-call () , entonces vemos que más adelante en el borrador del estándar en la sección 6.5.2.2 Llamadas a funciones el párrafo 10 dice:

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

Este progtwig también muestra un comportamiento indefinido ya que modifica pa más de una vez entre puntos de secuencia . Del borrador de la sección estándar 6.5 párrafo 2 :

Entre el punto de secuencia anterior y siguiente, un objeto tendrá su valor almacenado modificado a lo sumo por la evaluación de una expresión. Además, el valor anterior se leerá solo para determinar el valor que se almacenará.

cita los siguientes ejemplos de código como no definidos:

 i = ++i + 1; a[i++] = i; 

Es importante tener en cuenta que aunque el operador de coma introduce puntos de secuencia, la coma utilizada en las llamadas a función es un separador y no el comma operator . Si miramos la sección 6.5.17 operador de coma párrafo 2 dice:

El operando izquierdo de un operador de coma se evalúa como una expresión vacía; hay un punto de secuencia después de su evaluación.

pero el párrafo 3 dice:

EJEMPLO Como indica la syntax, el operador de coma (como se describe en esta subcláusula) no puede aparecer en contextos donde se usa una coma para separar elementos en una lista (como argumentos a funciones o listas de inicializadores ).

Sin saber esto, tener advertencias activadas con gcc usando al menos -Wall habría proporcionado un mensaje similar a:

 warning: operation on 'pa' may be undefined [-Wsequence-point] printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa)); ^ 

y por defecto clang advertirá con un mensaje similar a:

 warning: unsequenced modification and access to 'pa' [-Wunsequenced] printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa)); ~ ^ 

En general, es importante comprender cómo usar sus herramientas de la manera más efectiva, conocer las banderas disponibles para advertencias es importante, para gcc puede encontrar esa información aquí . Algunas banderas que son útiles y te ahorrarán muchos problemas a largo plazo y son comunes tanto para gcc como para clang son -Wextra -Wconversion -pedantic . Para entender el clang simultaneidad puede ser muy útil. Por ejemplo, -fsanitize=undefined capturará muchas instancias de comportamiento indefinido en el tiempo de ejecución.

Como ya se dijo, el orden en que se evalúan los argumentos de la función no se especifica, y no hay punto de secuencia entre la evaluación de los mismos. Como cambias pa al pasar cada argumento, cambias y lees pa dos veces entre dos puntos de secuencia. Eso es en realidad un comportamiento indefinido. Encontré una explicación muy buena en el manual de GCC, que creo que podría ser útil:

Los estándares C y C ++ definen el orden en que las expresiones en un progtwig C / C ++ se evalúan en términos de puntos de secuencia, que representan un orden parcial entre la ejecución de partes del progtwig: las ejecutadas antes del punto de secuencia y las ejecutadas después eso. Esto ocurre después de la evaluación de una expresión completa (una que no es parte de una expresión más grande), después de la evaluación del primer operando de a &&, ||,? : o, (coma) operador, antes de llamar a una función (pero después de la evaluación de sus argumentos y la expresión que denota la función llamada), y en ciertos otros lugares. Aparte de lo expresado por las reglas de punto de secuencia, no se especifica el orden de evaluación de las subexpresiones de una expresión. Todas estas reglas describen solo un orden parcial en lugar de un orden total, ya que, por ejemplo, si se invocan dos funciones dentro de una expresión sin punto de secuencia entre ellas, no se especifica el orden en que se invocan las funciones. Sin embargo, el comité de estándares ha dictaminado que las llamadas a función no se superponen.

No se especifica cuando entre los puntos de secuencia las modificaciones a los valores de los objetos surten efecto. Los progtwigs cuyo comportamiento depende de esto tienen un comportamiento indefinido; los estándares C y C ++ especifican que “entre el punto de secuencia anterior y el siguiente, un objeto tendrá su valor almacenado modificado 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á “. Si un progtwig rompe estas reglas, los resultados en cualquier implementación en particular son completamente impredecibles.

Los ejemplos de código con comportamiento indefinido son a = a ++ ;, a [n] = b [n ++] y a [i ++] = i ;. Algunos casos más complicados no son diagnosticados por esta opción, y puede dar un resultado falso positivo ocasional, pero en general se ha encontrado bastante efectivo para detectar este tipo de problema en los progtwigs.

El estándar está redactado de manera confusa, por lo tanto, existe cierto debate sobre el significado preciso de las reglas de puntos de secuencia en casos sutiles. Los enlaces a las discusiones sobre el problema, incluidas las definiciones formales propuestas, se pueden encontrar en la página de lecturas del CCG, en http://gcc.gnu.org/readings.html .

Modificar una variable más de una vez en una expresión es un comportamiento indefinido. Por lo tanto, puede obtener resultados diferentes en diferentes comstackdores. Por lo tanto, evite modificar una variable más de una vez.

La respuesta de Grant es correcta, no está definida.

PERO,,,

Con su ejemplo, su comstackdor parece estar evaluando en orden de derecha a izquierda (como era de esperar, el orden en que los argumentos se insertan en la stack). Si puede hacer otras pruebas para demostrar que la orden se mantiene de manera uniforme incluso con las optimizaciones habilitadas, y si solo va a seguir con esa versión del comstackdor, puede asumir de manera segura el orden de derecha a izquierda.

Sin embargo, es totalmente no portátil y algo horrible y horrible de hacer.