c++ - matematicas - ¿El orden de las operaciones cambia dentro de una expresión if?
orden de operaciones matematicas (6)
Recientemente me encontré con algo que pensé que entendía de inmediato, pero pensando más en él me gustaría entender por qué funciona de la manera que lo hace.
Considera el código a continuación. El (x-- == 9)
se está evaluando claramente, mientras que (y++ == 11)
no lo está. Lo primero que pensé fue que la lógica &&
kicks in, ve que la expresión ya se ha vuelto falsa, y patea antes de evaluar la segunda parte de la expresión.
Cuanto más lo pienso, más no entiendo por qué esto se comporta como lo hace. Según lo entiendo, los operadores lógicos caen por debajo de las operaciones de incremento en el orden de precedencia. ¿No debería (y++ == 11)
evaluarse, aunque la expresión general ya se haya vuelto falsa?
En otras palabras, ¿no debería el orden de las operaciones dictar que (y++ == 11)
se evalúe antes de que la instrucción if
dé cuenta de que la expresión como un todo será falsa?
#include <iostream>
using namespace std;
int main( int argc, char** argv )
{
int x = 10;
int y = 10;
if( (x-- == 9) && (y++ == 11) )
{
cout << "I better not get here!" << endl;
}
cout << "Final X: " << x << endl;
cout << "Final Y: " << y << endl;
return 0;
}
Salida:
Final X: 9
Final Y: 10
¿No debería
(y++ == 11)
evaluarse, aunque la expresión general ya se haya vueltofalse
?
No: el &&
y ||
operadores de cortocircuito: se evalúan de izquierda a derecha y tan pronto como se conoce el resultado de la expresión, la evaluación se detiene (es decir, tan pronto como se sabe que la expresión es false
en el caso de una serie de &&
, o true
en el caso de una serie de ||
) (*) .
No tiene sentido hacer un trabajo extra que no necesita hacerse. Este comportamiento de cortocircuito también es bastante útil y permite la escritura del código terser. Por ejemplo, dado un puntero a un objeto tipo struct, puede probar si el puntero es nulo y luego desreferenciar el puntero en una subexpresión posterior, por ejemplo: if (p && p->is_set) { /* ... */ }
.
(*) Tenga en cuenta que en C ++, puede sobrecargar tanto el &&
como el ||
para los operandos de clase y, si lo hace, pierden su propiedad de cortocircuito (generalmente no es aconsejable sobrecargar &&
y ||
por este motivo).
los operadores lógicos caen debajo de las operaciones de incremento en el orden de precedencia.
El orden de precedencia no es el orden de ejecución. Son conceptos completamente diferentes. El orden de precedencia solo afecta el orden de ejecución en la medida en que los operandos son evaluados antes que su operador, y el orden de precedencia ayuda a decirle cuáles son los operandos de cada operador.
Los operadores en cortocircuito son una excepción parcial incluso a la regla de que los operandos son evaluados antes que el operador, ya que evalúan el LHS, entonces el operador tiene que decir si evalúa el RHS o no, quizás se evalúa el RHS, entonces el resultado de el operador es computado.
No piense en operaciones de precedencia más alta "ejecutando primero". Piense en ellos "vinculantes más estrictos". ++
tiene una precedencia mayor que &&
, y en la expresión x ++ && y ++
, la precedencia del operador significa que el ++
"se une más firmemente" a y
que &&
, por lo que la expresión en general es equivalente a (x++) && (y++)
, no (x++ && y) ++
.
El estándar C no dicta ningún orden particular de evaluación de expresión en if. Entonces, el comportamiento será específico del compilador y usará este estilo de codificación no portátil. Se enfrenta a ese problema porque el aumento / disminución de valor es una operación posterior, pero el estándar dice que es una operación posterior a la expresión donde se usa la variable. Entonces, si un compilador considera que su expresión es solo uso de variables individuales como x o y, entonces verá un resultado. Si un compilador cree que la expresión es completa si se evalúa la expresión, entonces verá otro resultado. Espero que ayude.
La precedencia y la asociatividad no especifican el orden en que se realizan las operaciones. Especifican cómo se agrupan las operaciones: es decir, en la siguiente expresión:
x && y++
... la precedencia más baja de &&
dice que está agrupada como si fuera:
x && (y++)
en lugar de como
(x && y)++
En su expresión, la precedencia relativa de &&
y ++
no importa, porque de todos modos se han separado esos operadores con paréntesis.
El agrupamiento (y, por lo tanto, la precedencia y la asociatividad) especifica en qué valor opera cada operador; pero no especifica nada sobre cuándo lo hace.
Para la mayoría de los operadores, el orden en que se realizan las operaciones no está especificado; sin embargo, en el caso de &&
se especifica evaluar el operando de la mano izquierda primero, luego solo evaluar el operando de la mano derecha si el resultado del operando de la izquierda no fue -cero.
Los operadores condicionales evalúan de izquierda a derecha y se detienen tan pronto como se conoce el resultado (un AND con una falsedad o un OR con un valor verdadero).
No. El orden de precedencia simplemente decide si obtienes esto:
A && B
(con A
siendo x-- == 9
y B
siendo y++ == 11
) o
A == B == C
(con A
siendo x--
, B
siendo 9 && y++
, y C
siendo 11).
Obviamente, estamos lidiando con el primer caso. El cortocircuito se aplica completamente; si A
es verdadero, entonces B
no se evalúa.