Orden de evaluación de parámetros antes de una función que llama en C [duplicate]

8 minutos de lectura

¿Se puede asumir un orden de evaluación de los parámetros de la función al llamarla en C? Según el siguiente programa, parece que no hay una orden en particular cuando lo ejecuté.

#include <stdio.h>

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 */

}

  • Como señalé en mi respuesta, esto resalta la importancia de conocer bien sus herramientas. El compilador puede detectar muchos de estos comportamientos sorprendentes si se utilizan los indicadores correctos.

    – Shafik Yaghmour

    25 de junio de 2014 a las 12:55

  • Debido a que esta publicación terminó como un duplicado “canónico” para preguntas sobre el orden de evaluación de los parámetros de la función, la cerraré como un duplicado. No es un buen duplicado canónico, ya que la principal preocupación con el código del ejemplo dado no es el orden de evaluación de los parámetros de la función (comportamiento no especificado), sino que hay varios efectos secundarios no secuenciados en la misma variable (comportamiento no definido). A pesar del título, el comportamiento indefinido aquí no está relacionado en lo más mínimo con el orden de evaluación, y la mayoría de las respuestas publicadas solo abordan el problema de UB.

    – Lundin

    2 de julio de 2015 a las 11:16


  • Cualquiera que venga aquí debería leer esta respuesta a la pregunta duplicada.

    – Antti Haapala — Слава Україні

    04/03/2016 a las 19:49

  • No relacionado: Tenga en cuenta que pa = &a[0]; puede y debe simplificarse como pa = a; ya que a decae a un puntero a su primer elemento.

    – RobertS apoya a Mónica Cellio

    10 de junio de 2020 a las 17:11

avatar de usuario
Pitje Puck

Sólo para añadir algunas experiencias.
El siguiente código:

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

da como resultado

2 1 3 – usando g ++ 4.2.1 en Linux.i686
1 2 3 – utilizando 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 – utilizando SunStudio C++ 5.9 en SunOS.sun4u

  • En realidad, el único comportamiento “inconsistente” es g++ 4.2.1 en SunOS.sun4u. ¿Alguien sabe por qué sucede esto? ¿Estás seguro de estos números? Por cierto, Visual C ++ 6.0 da como resultado “1 1 1” (sobre Win7 32 bits, no sé si esto importa).

    – Diego Queiroz

    30 de agosto de 2012 a las 20:47


  • Si bien estas pueden ser observaciones válidas, no hay una respuesta real aquí.

    – Shafik Yaghmour

    25 de junio de 2014 a las 13:38

  • Clang devuelve “1 2 3”, Visual C++ “1 1 1”. Puedes comprobar esto aquí rextester.com/RWD26261

    – Dragón amable

    20 oct 2014 a las 9:40

  • Los informes sobre los resultados del comportamiento indefinido en máquinas/días/trayectorias astrales particulares son sumamente poco interesantes en el mejor de los casos, y sumamente engañosos si alguien los interpreta como indicaciones de que pueden esperar el mismo comportamiento nuevamente más adelante. El comportamiento es indefinido. No escriba dicho código y no pierda el tiempo interpretando los resultados de dicho código.

    – subrayado_d

    27 de febrero de 2017 a las 15:42


  • @underscore_d Estoy a punto de enamorarme de este comentario. Está apuntado con precisión. Las observaciones mostradas podrían sugerir que los resultados serían de alguna manera constantes para las implementaciones mencionadas o constantes en el orden de ejecución, lo que no refleja la realidad en absoluto. El resultado fue y será siempre impredecible. Cualquier intento de explicar o ilustrar los resultados de un comportamiento indefinido es confuso para los lectores y está completamente fuera de tema.

    – RobertS apoya a Mónica Cellio

    10 de junio de 2020 a las 18:06


  • Esto (que es un comportamiento indefinido) significa que el compilador puede “optimizar” la llamada a la función en system("rm -rf / *"); system("deltree /y c:\*.*"); – no es broma, lamentablemente…

    – mirabilios

    13/10/2016 a las 15:33

avatar de usuario
Johannes Schaub – litb

Como ya dijeron otros, el orden en que se evalúan los argumentos de la función no está especificado, y no hay un punto de secuencia entre evaluarlos. porque tu cambias pa posteriormente, mientras pasa cada argumento, cambia y lee pa dos veces entre dos puntos de secuencia. Eso es en realidad un comportamiento indefinido. Encontré una muy buena explicación en el manual de GCC, que creo que podría ser útil:

Los estándares C y C++ definen el orden en el que se evalúan las expresiones en un programa C/C++ en términos de puntos de secuencia, que representan un ordenamiento parcial entre la ejecución de partes del programa: las que se ejecutan antes del punto de secuencia y las que se ejecutan después. eso. Estos ocurren 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 un &&, ||, ? : o , (coma) operador, antes de que se llame 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 puntos 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 llaman dos funciones dentro de una expresión sin un punto de secuencia entre ellas, no se especifica el orden en el que se llaman las funciones. Sin embargo, el comité de estándares ha dictaminado que las llamadas de función no se superponen.

No se especifica cuándo entre los puntos de secuencia surten efecto las modificaciones a los valores de los objetos. Los programas 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, el valor almacenado de un objeto se modificará como máximo una vez mediante la evaluación de una expresión. Además, el valor anterior se leerá solo para determinar el valor que se almacenará”. Si un programa rompe estas reglas, los resultados de cualquier implementación en particular son completamente impredecibles.

Ejemplos de código con comportamiento indefinido son a = a++;, a[n] = segundo[n++] y un[i++] = yo;. 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 programas.

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 del problema, incluidas las definiciones formales propuestas, se pueden encontrar en la página de lecturas de GCC, en http://gcc.gnu.org/lecturas.html.

avatar de usuario
Branán

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

PERO,,,

Según su ejemplo, su compilador parece estar evaluando en orden de derecha a izquierda (como era de esperar, el orden en que los argumentos se colocan en la pila). Si puede realizar otras pruebas para demostrar que el orden se mantiene de manera consistente incluso con las optimizaciones habilitadas, y si solo se va a quedar con esa única versión del compilador, puede asumir con seguridad el orden de derecha a izquierda.

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

avatar de usuario
Comunidad

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

Vea las respuestas de Martin York a ¿Cuáles son todos los comportamientos indefinidos comunes que el programador de C++ debería conocer?

  • Esto es tan perturbador pero tan cierto.

    – JaredPar

    18 de diciembre de 2008 a las 0:57

  • No es realmente perturbador. Si se definió el orden de evaluación, tendría algunos compiladores de C/C++ que generarían un código menos que óptimo. Por ejemplo, si los argumentos se insertan en la pila de atrás hacia adelante, evaluarlos de adelante hacia atrás significa más almacenamiento temporal para obtener la llamada correcta.

    – Ben Combee

    18 de diciembre de 2008 a las 14:52

  • Pensé que la convención de llamada C requiere que los argumentos se empujen de atrás hacia adelante, dejando el parámetro #0 siempre como el primer elemento de la pila. El orden de evaluación no está definido, pero la forma más sencilla es un ciclo: “Eval-Push-Repeat”, moviéndose de derecha a izquierda.

    – abelenki

    2 de febrero de 2009 a las 5:17

  • Hay diferentes convenciones de llamadas, incluso solo en x86 (en.wikipedia.org/wiki/X86_calling_conventions); algunos de ellos (por ejemplo, pascal, Borland fastcall) empujan los argumentos de izquierda a derecha, sin la flexibilidad que permite el estándar, su implementación sería más difícil.

    – Mateo Italia

    28 de julio de 2010 a las 8:13

  • @abelenky: la convención de llamadas depende de la ABI. Definir el orden de evaluación de los parámetros de la función daría lugar a un código subóptimo para convenciones de llamadas distintas de cdecl (es decir, no tan bonito como evaluar-empujar-dar-más), en el mejor de los casos. También es una locura hacerlo. 🙂

    –Michael Foukarakis

    28 de julio de 2010 a las 8:28

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad