valor una solucion resuelto hallar graficar funciones funcion determinar como c++ c c++03 order-of-evaluation sequence-points

c++ - una - graficar funcion f(x)=x



¿El valor de x*f(x) no está especificado si f modifica x? (6)

El orden de evaluación de los operandos de casi todos los operadores de C ++ no está especificado. El compilador puede evaluar operandos en cualquier orden, y puede elegir otro orden cuando la misma expresión se evalúa nuevamente

Como el orden de evaluación no siempre es el mismo, puede obtener resultados inesperados.

http://en.cppreference.com/w/cpp/language/eval_order

He visto un montón de preguntas con respecto a los puntos de secuencia, y no he podido averiguar si el orden de evaluación para x*f(x) está garantizado si f modifica x , y esto es diferente para f(x)*x .

Considera este código:

#include <iostream> int fx(int &x) { x = x + 1; return x; } int f1(int &x) { return fx(x)*x; // Line A } int f2(int &x) { return x*fx(x); // Line B } int main(void) { int a = 6, b = 6; std::cout << f1(a) << " " << f2(b) << std::endl; }

Esto imprime 49 42 en g ++ 4.8.4 (Ubuntu 14.04).

Me pregunto si esto es un comportamiento garantizado o no especificado.

Específicamente, en este programa, se llama a fx dos veces, con x=6 ambas ocasiones, y devuelve 7 ambas veces. La diferencia es que la línea A calcula 7 * 7 (tomando el valor de x después de que fx regrese) mientras que la línea B calcula 6 * 7 (tomando el valor de x antes de que fx regrese).

¿Este comportamiento está garantizado? En caso afirmativo, ¿qué parte del estándar especifica esto?

Además: si cambio todas las funciones para usar int *x lugar de int &x y realizo los cambios correspondientes a los lugares desde los que se llaman, obtengo el código C que tiene los mismos problemas. ¿La respuesta es diferente para C?


Debes distinguir:

a) Prioridad y asociatividad del operador, que controla el orden en que los operadores combinan los valores de las subexpresiones.

b) La secuencia de evaluación de subexpresión. Por ejemplo, en la expresión f(x)/g(x) , el compilador puede evaluar g(x) primero f(x) después. No obstante, el valor resultante debe calcularse dividiendo los respectivos subvalores en el orden correcto, por supuesto.

c) La secuencia de efectos secundarios de las subexpresiones. En términos generales, por ejemplo, el compilador podría, en aras de la optimización, decidir escribir valores en las variables afectadas solo al final de la expresión o en cualquier otro lugar adecuado.

Como aproximación muy aproximada, puede decir que dentro de una sola expresión, el orden de evaluación (no asociatividad, etc.) es más o menos no especificado. Si necesita un orden de evaluación específico, descomponga la expresión en series de declaraciones como esta:

int a = f(x); int b = g(x); return a/b;

en lugar de

return f(x)/g(x);

Para ver las reglas exactas, consulte http://en.cppreference.com/w/cpp/language/eval_order


El orden de evaluación de los argumentos no está especificado por el estándar, por lo que el comportamiento que ve no está garantizado.

Como mencionas los puntos de secuencia, consideraré el estándar c ++ 03 que usa ese término, mientras que los estándares posteriores cambiaron la redacción y abandonaron el término.

ISO / IEC 14882: 2003 (E) §5 / 4:

Excepto cuando se indique lo contrario, el orden de evaluación de operandos de operadores individuales y subexpresiones de expresiones individuales, y el orden en que se producen los efectos secundarios, no se especifica ...

También se discute si este es un comportamiento indefinido o si el orden es meramente no especificado. El resto de ese párrafo arroja algo de luz (o duda) sobre eso.

ISO / IEC 14882: 2003 (E) §5 / 4:

... Entre el punto de secuencia anterior y siguiente, un objeto escalar tendrá su valor almacenado modificado como máximo una vez por la evaluación de una expresión. Además, se accederá al valor anterior solo para determinar el valor que se almacenará. Los requisitos de este párrafo se cumplirán para cada orden permisible de las subexpresiones de una expresión completa; de lo contrario, el comportamiento no está definido.

x se modifica en f y su valor se lee como un operando en la misma expresión donde se llama f . Y no se especifica si x lee el valor modificado o no modificado. Eso podría gritar ¡Comportamiento indefinido! para usted, pero mantenga sus caballos, porque el estándar también establece:

ISO / IEC 14882: 2003 (E) §1.9 / 17:

... Al llamar a una función (ya sea que la función esté en línea o no), hay un punto de secuencia después de la evaluación de todos los argumentos de funciones (si los hay) que tienen lugar antes de la ejecución de cualquier expresión o declaración en el cuerpo de la función. También hay un punto de secuencia después de la copia de un valor devuelto y antes de la ejecución de cualquier expresión fuera de la función 11) ...

Entonces, si f(x) se evalúa primero, entonces hay un punto de secuencia después de copiar el valor devuelto. Entonces, la regla anterior sobre UB no se aplica porque la lectura de x no está entre el punto de secuencia siguiente y anterior. El x operando tendrá el valor modificado.

Si x se evalúa primero, entonces hay un punto de secuencia después de evaluar los argumentos de f(x) De nuevo, la regla sobre UB no se aplica. En este caso, x operando tendrá el valor no modificado.

En resumen, el orden no está especificado, pero no hay un comportamiento indefinido . Es un error, pero el resultado es predecible hasta cierto punto. El comportamiento es el mismo en los estándares posteriores, aunque la redacción cambió. No profundizaré en ellos ya que ya está bien cubierto en otras buenas respuestas.

Ya que preguntas sobre una situación similar en C

C89 (borrador) 3.3 / 3:

Salvo lo indicado por la sintaxis 27 o especificado de otra manera más adelante (para el operador de llamada de función (), &&, ||,?:, Y operadores de coma), el orden de evaluación de las subexpresiones y el orden en que se producen los efectos secundarios son ambos no especificados.

La excepción de llamada de función ya se menciona aquí. A continuación se encuentra el párrafo que implica el comportamiento indefinido si no hubiera puntos de secuencia:

C89 (borrador) 3.3 / 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, se accederá al valor anterior solo para determinar el valor que se almacenará. 26

Y aquí están los puntos de secuencia definidos:

C89 (borrador) A.2

Los siguientes son los puntos de secuencia descritos en 2.1.2.3

  • La llamada a una función, después de que los argumentos hayan sido evaluados (3.3.2.2).

  • ...

  • ... la expresión en una declaración de retorno (3.6.6.4).

Las conclusiones son las mismas que en C ++.


En la expresión x * y , los términos y son secuenciados . Esta es una de las tres posibles relaciones de secuenciación, que son:

  • A secuencia anterior a B : A debe evaluarse, con todos los efectos secundarios completos, antes de que B comience la evaluacióng
  • A y B con secuencia indeterminada : uno de los dos casos siguientes es verdadero: A se secuencia, antes de B , o B se secuencia, antes de A No se especifica cuál de esos dos casos tiene.
  • A y B no secuenciados: no hay una relación de secuencia definida entre A y B

Es importante tener en cuenta que estas son relaciones de pareja . No podemos decir " x es secuenciado". Solo podemos decir que dos operaciones no son secuenciadas una con respecto a la otra.

También es importante que estas relaciones sean transitive ; y las últimas dos relaciones son simétricas.

no especificado es un término técnico que significa que el Estándar especifica un número determinado de resultados posibles. Esto es diferente al comportamiento indefinido, lo que significa que el Estándar no cubre el comportamiento en absoluto. Mira aquí para leer más.

Moviéndose al código x * f(x) . Esto es idéntico a f(x) * x , porque como se discutió anteriormente, x y f(x) son secuenciados , con respecto a cada uno, en ambos casos.

Ahora llegamos al punto en el que parece que varias personas se están despegando. La evaluación de la expresión f(x) se sigue con respecto a x . Sin embargo, no se sigue que cualquier declaración dentro del cuerpo de la función de f tampoco sea secuenciada con respecto a x . De hecho, existen relaciones de secuenciación que rodean cualquier llamada de función, y esas relaciones no se pueden ignorar.

Aquí está el texto de C ++ 14:

Cuando se llama a una función (esté o no en línea), cada cómputo de valor y efecto secundario asociado con cualquier expresión de argumento, o con la expresión de postfijo que designa la función llamada, se secuencia antes de la ejecución de cada expresión o declaración en el cuerpo del llamada función. [Nota: los cómputos de valor y los efectos secundarios asociados con las diferentes expresiones de argumentos no se han secuenciado. -Finalización] Toda evaluación en la función de llamada (incluidas otras llamadas a función) que no está secuenciada específicamente antes o después de la ejecución del cuerpo de la función llamada se secuencia indeterminadamente con respecto a la ejecución de la función llamada.

con nota al pie:

En otras palabras, las ejecuciones de funciones no se intercalan entre sí.

El texto en negrita establece claramente que para las dos expresiones:

  • A : x = x + 1; dentro de f(x)
  • B : evaluando la primera x en la expresión x * f(x)

su relación es: secuencia indeterminada .

El texto sobre el comportamiento indefinido y la secuencia es:

Si un efecto secundario en un objeto escalar no se está secuenciando en relación con otro efecto secundario en el mismo objeto escalar o un cálculo de valor utilizando el valor del mismo objeto escalar, y no son potencialmente concurrentes (1.10), el comportamiento no está definido.

En este caso, la relación está indeterminadamente secuenciada , no sin secuenciar. Entonces no hay un comportamiento indefinido.

El resultado, en cambio, no se especifica según si x se secuencia antes de x = x + 1 o al revés. Entonces solo hay dos resultados posibles, 42 y 49 .

En caso de que alguien tenga dudas sobre la x en f(x) , se aplica el siguiente texto:

Cuando se llama a una función (esté o no en línea), cada cómputo de valor y efecto secundario asociado con cualquier expresión de argumento, o con la expresión de postfijo que designa la función llamada, se secuencia antes de la ejecución de cada expresión o declaración en el cuerpo del llamada función.

Entonces la evaluación de esa x se secuencia antes de x = x + 1 . Este es un ejemplo de un evlauation que cae bajo el caso de " secuenciado específicamente antes " en la cita en negrita de arriba.

Nota al pie: el comportamiento fue exactamente el mismo en C ++ 03, pero la terminología era diferente. En C ++ 03 decimos que hay un punto de secuencia a la entrada y salida de cada llamada de función, por lo tanto, la escritura en x dentro de la función está separada de la lectura de x fuera de la función por al menos un punto de secuencia.


En términos de secuencia de evaluación, es más fácil pensar en x*f(x) como si fuera:

operator*(x, f(x));

para que no haya preconceptos matemáticos sobre cómo se supone que funciona la multiplicación.

Como señaló @ dan04 amablemente, el estándar dice:

Sección 1.9.15: "Excepto cuando se indique lo contrario, las evaluaciones de operandos de operadores individuales y de subexpresiones de expresiones individuales no se han secuenciado".

Esto significa que el compilador puede evaluar estos argumentos en cualquier orden, siendo el punto de secuencia operator* call. La única garantía es que antes de llamar al operator* , ambos argumentos deben ser evaluados.

En su ejemplo, conceptualmente, puede estar seguro de que al menos uno de los argumentos será 7, pero no puede estar seguro de que ambos lo harán. Para mí, esto sería suficiente para etiquetar este comportamiento como indefinido; sin embargo, la respuesta @ user2079303 explica bien por qué no es técnicamente el caso.

Independientemente de si el comportamiento es indefinido o indeterminado, no puede usar dicha expresión en un programa de buen comportamiento.


Una nota rápida sobre algo que no veo cubierto explícitamente por las otras respuestas:

si el orden de evaluación para x*f(x) está garantizado si f modifica x , y esto es diferente para f(x)*x .

Considere, como en la respuesta de Maksim

operator*(x, f(x));

ahora solo hay dos formas de evaluar ambos argumentos antes de la llamada según sea necesario:

auto lhs = x; // or auto rhs = f(x); auto rhs = f(x); // or auto lhs = x; return lhs * rhs

Entonces, cuando preguntas

Me pregunto si esto es un comportamiento garantizado o no especificado.

el estándar no especifica cuál de esos dos comportamientos debe elegir el compilador, pero especifica que esos son los únicos comportamientos válidos.

Por lo tanto, no está garantizado ni completamente no especificado.

Oh y:

He visto un montón de preguntas con respecto a los puntos de secuencia, y no he podido averiguar si el orden de evaluación ...

los puntos de secuencia se usan en el tratamiento de este estándar de lenguaje C, pero no en el estándar de C ++.