Pregunta

¿El estándar ANSI exige que los operadores lógicos se cortocircuiten, ya sea en C o C ++?

Estoy confundido porque recuerdo el libro K & amp; R que dice que su código no debería depender de que estas operaciones estén en cortocircuito, ya que puede que no. ¿Podría alguien indicar dónde en el estándar se dice que las operaciones lógicas siempre están en cortocircuito? Estoy principalmente interesado en C ++, una respuesta también para C sería genial.

También recuerdo haber leído (no recuerdo dónde) que el orden de evaluación no está estrictamente definido, por lo que su código no debería depender o asumir que las funciones dentro de una expresión se ejecutarían en un orden específico: al final de una declaración todas las funciones referenciadas se habrán llamado, pero el compilador tiene libertad para seleccionar el orden más eficiente.

¿El estándar indica el orden de evaluación de esta expresión?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
¿Fue útil?

Solución

Sí, se requiere orden de cortocircuito y evaluación para los operadores || y & amp; & amp; en los estándares C y C ++.

El estándar C ++ dice (debe haber una cláusula equivalente en el estándar C):

  

1.9.18

     

En la evaluación de las siguientes expresiones

a && b
a || b
a ? b : c
a , b
     

utilizando el significado incorporado de los operadores en estas expresiones, hay un punto de secuencia después de la evaluación de la primera expresión (12).

En C ++ hay una trampa adicional: el cortocircuito NO se aplica a los tipos que sobrecargan a los operadores || y & amp; & amp; .

  

Nota a pie de página 12: Los operadores indicados en este párrafo son los operadores integrados, como se describe en la cláusula 5. Cuando uno de estos operadores está sobrecargado (cláusula 13) en un contexto válido, designando así un usuario definido función de operador, la expresión designa una invocación de función, y los operandos forman una lista de argumentos, sin un punto de secuencia implícito entre ellos.

Por lo general, no se recomienda sobrecargar estos operadores en C ++ a menos que tenga un requisito muy específico. Puede hacerlo, pero puede romper el comportamiento esperado en el código de otras personas, especialmente si estos operadores se usan indirectamente a través de plantillas de instanciación con el tipo que sobrecarga a estos operadores.

Otros consejos

La evaluación de cortocircuito y el orden de evaluación es un estándar semántico obligatorio tanto en C como en C ++.

Si no fuera así, un código como este no sería un idioma común

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

Sección 6.5.13 Operador lógico Y de la especificación C99 (enlace PDF) dice

  

(4). A diferencia del binario bit a bit & amp; operador, el & amp; & amp; garantías del operador   evaluación de izquierda a derecha; hay un   punto de secuencia después de la evaluación de   El primer operando. Si el primero   operando se compara igual a 0, el   segundo operando no se evalúa.

Del mismo modo, la sección 6.5.14 Operador lógico OR dice

  

(4) A diferencia del bit a bit | operador, el ||   operador garantiza de izquierda a derecha   evaluación; hay un punto de secuencia   después de la evaluación de la primera   operando Si el primer operando se compara   desigual a 0, el segundo operando es   no evaluado.

Se puede encontrar una redacción similar en los estándares de C ++, verificar sección 5.14 en este borrador de copia . Como las fichas de notas en otra respuesta, si anula & amp; & amp; o ||, entonces ambos operandos deben evaluarse a medida que se convierte en una llamada de función regular.

Sí, lo exige (tanto el orden de evaluación como el cortocircuito). En su ejemplo, si todas las funciones devuelven verdadero, el orden de las llamadas es estrictamente de la función A, luego de la función B y luego de la función C. Utilizado para esto como

if(ptr && ptr->value) { 
    ...
}

Lo mismo para el operador de coma:

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b()); 

Uno dice entre el operando izquierdo y derecho de & amp; & amp; , || , , y entre el primero y el segundo / tercero El operando de ?: (operador condicional) es un "punto de secuencia". Cualquier efecto secundario se evalúa completamente antes de ese punto. Entonces, esto es seguro:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

Tenga en cuenta que el operador de coma no debe confundirse con la coma sintáctica utilizada para separar las cosas:

// order of calls to a and b is unspecified!
function(a(), b());

El estándar C ++ dice en 5.14 / 1 :

  

El & amp; & amp; grupos de operadores de izquierda a derecha. Los operandos se convierten implícitamente a tipo bool (cláusula 4).   El resultado es verdadero si ambos operandos son verdaderos y falsos de lo contrario. A diferencia de & amp ;, & amp; & amp; garantiza de izquierda a derecha   evaluación: el segundo operando no se evalúa si el primer operando es falso.

Y en 5.15 / 1 :

  

El || grupos de operadores de izquierda a derecha. Los operandos se convierten implícitamente a bool (cláusula 4). Devuelve verdadero si alguno de sus operandos es verdadero y falso en caso contrario. A diferencia de |, || garantiza la evaluación de izquierda a derecha; además, el segundo operando no se evalúa si el primer operando se evalúa como verdadero.

Dice para ambos al lado de esos:

  

El resultado es un bool. Todos los efectos secundarios de la primera expresión, excepto la destrucción de los temporales (12.2) suceden antes de evaluar la segunda expresión.

Además de eso, 1.9 / 18 dice

  

En la evaluación de cada una de las expresiones

     
      
  • a & amp; & amp; b
  •   
  • a || b
  •   
  • a? b: C
  •   
  • a, b
  •   
     

usando el significado incorporado de los operadores en estas expresiones (5.14, 5.15, 5.16, 5.18), hay un punto de secuencia después de la evaluación de la primera expresión.

Directamente del viejo K & amp; R:

  

C garantiza que & amp; & amp; y || se evalúan de izquierda a derecha & # 8212; pronto veremos casos en los que esto importa.

Ten mucho, mucho cuidado.

Para los tipos fundamentales, estos son operadores de acceso directo.

Pero si define estos operadores para su propia clase o tipos de enumeración, no son atajos. Debido a esta diferencia semántica en su uso en estas circunstancias diferentes, se recomienda que no defina estos operadores.

Para el operador & amp; & amp; y operator || para los tipos fundamentales, el orden de evaluación es de izquierda a derecha (de lo contrario, sería difícil un atajo :-) Pero para operadores sobrecargados que defina, estos son básicamente azúcar sintáctico para definir un método y, por lo tanto, el orden de evaluación de los parámetros no está definido.

Si confías en Wikipedia:

  

[ & amp; & amp; y || ] son ??semánticamente distintos de los operadores de bit-wise & amp; y | porque nunca evaluarán el operando correcto si el resultado se puede determinar solo desde la izquierda

http://en.wikipedia.org/wiki/C_(programming_language) # Características

Su pregunta se reduce a precedencia del operador C ++ y asociatividad. Básicamente, en expresiones con múltiples operadores y sin paréntesis, el compilador construye el árbol de expresión siguiendo estas reglas.

Por precedencia, cuando tiene algo como A op1 B op2 C , puede agrupar cosas como (A op1 B) op2 C o A op1 (B op2 C) . Si op1 tiene mayor prioridad que op2 , obtendrá la primera expresión. De lo contrario, obtendrá el segundo.

Para asociatividad, cuando tiene algo como A op B op C , puede volver a agrupar los delgados como (A op B) op C o A op (B op C) . Si op ha dejado asociatividad, terminamos con la primera expresión. Si tiene la asociatividad correcta, terminamos con la segunda. Esto también funciona para operadores en el mismo nivel de precedencia.

En este caso particular, & amp; & amp; tiene mayor prioridad que || , por lo que la expresión se evaluará como (a! = " " ; & amp; & amp; it == seqMap.end ()) || isEven .

El pedido en sí es " de izquierda a derecha " en la forma del árbol de expresión. Entonces, primero evaluaremos a! = & Quot; " & amp; & amp; it == seqMap.end () . Si es cierto, toda la expresión es verdadera, de lo contrario vamos a isEven . El procedimiento se repite recursivamente dentro de la subexpresión izquierda, por supuesto.


Tidbits interesantes, pero el concepto de precedencia tiene sus raíces en la notación matemática. Lo mismo sucede en a * b + c , donde * tiene mayor prioridad que + .

Aún más interesante / obscuro, para una expresión no parentestada A1 op1 A2 op2 ... opn-1 An , donde todos los operadores tienen la misma precedencia, el número de árboles de expresión binaria que podríamos formar es dado por los llamados números catalanes . Para grandes n , estos crecen extremadamente rápido. d

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top