2009-03-07 9 views
52

Bien, soy consciente de que el estándar dicta que una implementación de C++ puede elegir en qué orden se evalúan los argumentos de una función, pero ¿hay implementaciones que realmente "aprovechen" este en un escenario donde realmente afectaría el programa?Compiladores y orden de evaluación de los argumentos en C++

Ejemplo clásico:

int i = 0; 
foo(i++, i++); 

Nota: No estoy buscando a alguien que me diga que el orden de evaluación no se puede confiar en, soy muy consciente de ello. Solo me interesa saber si algún compilador realmente evalúa de una orden de izquierda a derecha porque supongo que si lo hicieran se romperían muchos códigos mal escritos (con razón, pero probablemente se quejarían).

+0

Pruébelo usted mismo con diferentes compiladores? – strager

+0

Me hice la misma pregunta al implementar un intérprete para un subconjunto de C++. Uf. –

+6

Tenga en cuenta que 'foo (i ++, i ++)' invoca un comportamiento indefinido, porque 'i' se incrementa más de uno sin intervenir ningún punto de secuencia. – Nawaz

Respuesta

49

Depende del tipo de argumento, la convención de llamada de la función llamada, la arquitectura y el compilador. En un x86, la convención de llamadas Pascal evalúa los argumentos de izquierda a derecha, mientras que en la convención de llamadas C (__cdecl) es de derecha a izquierda. La mayoría de los programas que se ejecutan en múltiples plataformas tienen en cuenta las convenciones de llamadas para saltarse sorpresas.

Hay un lindo article en el blog de Raymond Chen si está interesado. También puede consultar la sección Stack and Calling del manual de GCC.

Edición: Mientras sepamos los pelos: Mi respuesta no se trata de una cuestión de idioma sino de una plataforma. El estándar de idioma no garantiza o prefiere uno sobre el otro y lo deja como no especificado. Tenga en cuenta la redacción. No dice que esto no está definido. Sin especificar en este sentido significa algo con lo que no puede contar, el comportamiento no portátil. No tengo la especificación C/proyecto útil pero debe ser similar a la de mi proyecto n2798 (C++) se describen

Algunos otros aspectos y operaciones de la máquina abstracta en esta norma como no especificada (por ejemplo, orden de evaluación de argumentos para una función). Donde sea posible, este Estándar Internacional define un conjunto de comportamientos permitidos. Estos definen los aspectos no deterministas de la máquina abstracta. Una instancia de la máquina abstracta puede así tener más de una secuencia de ejecución posible para un programa dado y una entrada dada.

+11

Para que quede claro, pascal/cdecl decide si los argumentos * se pasan * de izquierda a derecha o de derecha a izquierda; aún se pueden * evaluar * en cualquier orden. (Aunque debido a la presión de registro es probable que se evalúen en el mismo orden en que se aprobaron). –

+8

Incorrecto: los argumentos C se insertan en la pila de derecha a izquierda. Pero el orden en que se evalúan no está definido. –

+1

@Martin York: ¿Cuándo dije que el orden está bien definido? – dirkgently

1

última vez que vi diferencias fue entre VS2005 y GCC 3.x en un hardware x86 en 2007. Así que es (era?) Una situación muy probable. Así que nunca confío más en la orden de evaluación. Tal vez es mejor ahora.

+0

¿Oh? Eso es muy bueno saber Gracias. Actualmente, los únicos compiladores que uso son VC 08 e ICC 11. Probablemente también debería comenzar a probar mi código en GCC. – RaptorFactor

+2

ICC hace grandes esfuerzos para ser tan compatible con VS como sea posible. Buenas personas, esas personas Intel :) –

+0

VS tampoco garantiza una orden de evaluación. Han cambiado la forma en que evalúan los argumentos de función antes, y no hay ninguna razón por la que no lo hicieran de nuevo. No es algo en lo que deberías confiar. – jalf

2

Espero que la mayoría de los compiladores modernos intenten intercalar las instrucciones de cálculo de los argumentos, dado que el estándar C++ exige que sean independientes y por lo tanto carezcan de interdependencias. Hacer esto debería ayudar a mantener llenas las unidades de ejecución de una CPU profundamente canalizada y por lo tanto aumentar el rendimiento. (Por lo menos yo esperaría que un compilador que dice ser un compilador de optimización lo haría cuando se dan parámetros de optimización.)

6

Read this

No es una copia exacta de su pregunta, pero mi respuesta (y una algunos otros) cubra su pregunta también.

Existen muy buenas razones de optimización por las que el compilador puede no solo elegir de derecha a izquierda sino también entrelazarlas.

El estándar ni siquiera garantiza un orden secuencial. Es solo garantiza que cuando se llame a la función, todos los argumentos se hayan evaluado completamente.

Y sí, he visto algunas versiones de GCC hacer exactamente esto. Para su ejemplo, se llamaría foo (0,0) y yo sería 2 después. (No puedo darle el número de versión exacto del compilador. Fue hace un tiempo, pero no me sorprendería ver este comportamiento de nuevo. Es una manera eficiente de programar instrucciones)

+0

eliminación de subexpresiones comunes es la razón principal para la optimización de reordenamiento. Recuerde que la evaluación de argumentos y el paso de parámetros pueden ser diferentes, aunque muchos compiladores los tratan de forma idéntica. – plinth

3

Todos los argumentos son evaluados Orden no definida (según el estándar). Pero todas las implementaciones de C/C++ (que yo sepa) evalúan los argumentos de función desde de derecha a izquierda. EDITAR: CLang es una excepción (ver comentario a continuación).

Creo que el orden de evaluación de derecha a izquierda ha sido muy muy antiguo (desde los primeros compiladores de C). Ciertamente, mucho antes de que se inventara C++, y la mayoría de las implementaciones de C++ mantendrían el mismo orden de evaluación porque las primeras implementaciones de C++ simplemente se tradujeron en C.

Existen razones técnicas para evaluar los argumentos de las funciones de derecha a izquierda. En las arquitecturas de pila, los argumentos generalmente se insertan en la pila. En C/C++, puede llamar a una función con más argumentos de los que realmente se especificaron: los argumentos adicionales se ignoran simplemente. Si los argumentos se evalúan de izquierda a derecha y se presionan de izquierda a derecha, la ranura de la pila justo debajo del puntero de la pila contendrá el último argumento, y no hay forma de que la función obtenga el desplazamiento de un argumento en particular (porque la cantidad real de argumentos empujados depende de la persona que llama).

En una orden de inserción de derecha a izquierda, la ranura de pila justo debajo del puntero de pila siempre mantendrá el primer argumento, y la siguiente ranura contendrá el segundo argumento, etc. Las compensaciones de argumento siempre serán determinísticas para la función (que puede escribirse y compilarse en otra parte en una biblioteca, por separado de donde se llame).

Ahora, la orden de inserción de derecha a izquierda no ordena la orden de evaluación de derecha a izquierda, pero en los primeros compiladores, la memoria es escasa. En el orden de evaluación de derecha a izquierda, la misma pila se puede utilizar en el lugar (esencialmente, después de evaluar el argumento, que puede ser una expresión o una llamada de función), el valor de retorno ya está en la posición correcta en el apilar). En la evaluación de izquierda a derecha, los valores del argumento se deben almacenar por separado y se deben volver a la pila en orden inverso.

+0

clang utiliza la evaluación de izquierda a derecha. – Kaiserludi

8

Encontré la respuesta en c++ standards.

párrafo 5.2.2.8:

El orden de evaluación de argumentos no se especifica. Todos los efectos secundarios de las evaluaciones de expresión de argumentos surten efecto antes de que se ingrese la función. El orden de evaluación de la expresión de postfijo y la lista de expresión de argumento es no especificado.

En otras palabras, depende del compilador solamente.

+2

Um, no responde a la pregunta. – ThomasMcLeod

Cuestiones relacionadas