Question

La norme ANSI oblige-t-elle les opérateurs logiques à être court-circuités, en C ou C ++?

Je suis confus car je me souviens du livre K & amp; R disant que votre code ne devrait pas dépendre du court-circuitage de ces opérations, car elles ne le pourraient pas. Quelqu'un pourrait-il indiquer si, dans la norme, les opérations logiques sont toujours court-circuitées? Je suis surtout intéressé par le C ++, une réponse aussi pour le C serait formidable.

Je me souviens aussi d'avoir lu (je ne me souviens plus où) que l'ordre d'évaluation n'est pas strictement défini. Votre code ne devrait donc pas dépendre ni supposer que les fonctions au sein d'une expression seraient exécutées dans un ordre spécifique: à la fin d'une instruction. toutes les fonctions référencées auront été appelées, mais le compilateur est libre de choisir l'ordre le plus efficace.

Le standard indique-t-il l'ordre d'évaluation de cette expression?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
Était-ce utile?

La solution

Oui, un court-circuit et un ordre d'évaluation sont requis pour les opérateurs || et & amp; & amp; dans les normes C et C ++.

Le standard C ++ dit (il devrait y avoir une clause équivalente dans le standard C):

  

1.9.18

     

Dans l'évaluation des expressions suivantes

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

reprenant la signification intrinsèque des opérateurs dans ces expressions, il existe un point de séquence après l'évaluation de la première expression (12).

En C ++, il existe un piège supplémentaire: les courts-circuits NOT ne s'appliquent pas aux types qui surchargent les opérateurs || et & amp; & amp; .

  

Note de bas de page 12: les opérateurs indiqués dans ce paragraphe sont les opérateurs intégrés, tels que décrits dans la clause 5. Lorsque l'un de ces opérateurs est surchargé (clause 13) dans un contexte valide, désignant ainsi un utilisateur fonction opérateur, l'expression désigne une invocation de fonction et les opérandes forment une liste d'arguments, sans point de séquence implicite entre eux.

Il n'est généralement pas recommandé de surcharger ces opérateurs en C ++ sauf si vous avez une exigence très spécifique. Vous pouvez le faire, mais cela peut rompre le comportement attendu dans le code d’autres personnes, en particulier si ces opérateurs sont utilisés indirectement via des modèles d’instanciation avec le type surchargeant ces opérateurs.

Autres conseils

L’évaluation des courts-circuits et leur ordre d’évaluation sont des normes sémantiques obligatoires en C et C ++.

Si ce n'était pas le cas, un code comme celui-ci ne serait pas un idiome commun

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

   }

Section 6.5.13 Opérateur AND logique de la spécification C99 (lien PDF) dit

  

(4). Contrairement au binaire au niveau des bits & amp; opérateur, le & amp; & amp; garanties de l'opérateur   évaluation de gauche à droite; Il y a un   point de séquence après l'évaluation de   le premier opérande. Si le premier   l’opérande est égal à 0, le   le second opérande n'est pas évalué.

De même, la section 6.5.14 de l'opérateur logique OR indique

  

(4) Contrairement au bitwise | opérateur, le ||   opérateur garantit de gauche à droite   évaluation; il y a un point de séquence   après l'évaluation du premier   opérande. Si le premier opérande compare   inégale à 0, le deuxième opérande est   non évalué.

Un libellé similaire peut être trouvé dans les normes C ++, check section 5.14 de ce brouillon . Comme le note le vérificateur dans une autre réponse, si vous remplacez & amp; & amp; ou ||, les deux opérandes doivent être évalués car ils deviennent un appel de fonction normal.

Oui, cela est obligatoire (ordre d’évaluation et court-circuit). Dans votre exemple, si toutes les fonctions retournent la valeur true, l'ordre des appels provient strictement de la fonction A, puis de la fonction B, puis de la fonction C. Utilisé pour cela comme

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

Idem pour l'opérateur virgule:

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

On dit entre les opérandes gauche et droit de & amp; & amp; , || , , et entre les premier et deuxième / troisième L'opérande de ?: (opérateur conditionnel) est un "point de séquence". Tous les effets secondaires sont évalués complètement avant ce point. Donc, c'est sûr:

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

Notez que l'opérateur de virgule ne doit pas être confondu avec la virgule syntaxique utilisée pour séparer les éléments:

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

La norme C ++ dit dans 5.14 / 1 :

  

Le & amp; & amp; groupes d'opérateurs de gauche à droite. Les opérandes sont tous deux implicitement convertis en type bool (clause 4).   Le résultat est vrai si les deux opérandes sont vrais et faux sinon. Contrairement à & amp ;, & amp; & amp; garanties de gauche à droite   evaluation: le second opérande n'est pas évalué si le premier opérande est faux.

Et dans 5.15 / 1 :

  

Le || groupes d'opérateurs de gauche à droite. Les opérandes sont tous deux implicitement convertis en bool (clause 4). Il renvoie true si l'un de ses opérandes est true et false sinon. Contrairement à |, || garantit une évaluation de gauche à droite; de plus, le deuxième opérande n'est pas évalué si le premier opérande est évalué à vrai.

Cela dit pour les deux à côté de ceux-ci:

  

Le résultat est un bool. Tous les effets secondaires de la première expression, à l’exception de la destruction des temporaires (12.2), se produisent avant l’évaluation de la deuxième expression.

En outre, 1.9 / 18 dit

  

Dans l'évaluation de chacune des expressions

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

En utilisant la signification intégrée des opérateurs dans ces expressions (5.14, 5.15, 5.16, 5.18), il existe un point de séquence après l'évaluation de la première expression.

Tout droit du bon vieux K & R:

  

C garantit que & amp; & amp; et || sont évalués de gauche à droite - nous verrons bientôt des cas où cela compte.

Soyez très très prudent.

Pour les types fondamentaux, il s'agit d'opérateurs de raccourcis.

Mais si vous définissez ces opérateurs pour votre propre classe ou types d'énumération, ils ne sont pas des raccourcis. En raison de cette différence sémantique dans leur utilisation dans ces différentes circonstances, il est recommandé de ne pas définir ces opérateurs.

Pour l'opérateur & amp; & amp; et l ' opérateur || pour les types fondamentaux, l'ordre d'évaluation est laissé de droite (sinon, une coupe courte serait difficile :-) Mais pour Opérateurs surchargés que vous définissez, il s’agit essentiellement d’un sucre syntaxique permettant de définir une méthode et donc l’ordre d’évaluation des paramètres n’est pas défini.

Si vous faites confiance à Wikipedia:

  

[ & amp; & amp; et || ] sont sémantiquement distincts des opérateurs binaires et | car ils n'évalueront jamais l'opérande de droite si le résultat peut être déterminé par la gauche seule

http://en.wikipedia.org/wiki/C_(programming_language) #Caractéristiques

Votre question se résume à la priorité des opérateurs C ++ et à leur associativité. Fondamentalement, dans les expressions avec plusieurs opérateurs et sans parenthèses, le compilateur construit l’arbre des expressions en suivant ces règles.

Par priorité, quand vous avez quelque chose comme A op1 B op2 C , vous pouvez grouper les choses comme suit: (A op1 B) op2 C ou A op1 (B op2 C) . Si op1 a une priorité supérieure à op2 , vous obtiendrez la première expression. Sinon, vous aurez le deuxième.

Pour l’associativité, lorsque vous avez quelque chose comme A op B op C , vous pouvez à nouveau grouper minces comme (A op B) op C ou A op (B op C) . Si op a quitté l'associativité, nous aboutissons à la première expression. Si elle a une bonne associativité, nous nous retrouvons avec la seconde. Cela fonctionne également pour les opérateurs du même niveau de priorité.

Dans ce cas particulier, & amp; a une priorité supérieure à || , ainsi l'expression sera évaluée en tant que (a! = """ ; & amp; & amp; it == seqMap.end ()) || isEven .

La commande elle-même est "gauche à droite". sur la forme de l'arbre d'expression. Nous allons donc d'abord évaluer a! = "& Quot;". & amp; & amp; it == seqMap.end () . Si c'est vrai, l'expression entière est vraie, sinon nous allons aller à isEven . La procédure se répète de manière récursive dans la sous-expression gauche bien sûr.

Tidbits intéressants, mais le concept de priorité a ses racines dans la notation mathématique. La même chose se produit dans a * b + c , où * a une priorité supérieure à + .

Encore plus intéressant / obscur, pour une expression sans parenthèses A1 op1 A2 op2 ... opn-1 Un , où tous les opérateurs ont la même priorité, le nombre d’arbres d’expression binaires que nous pourrions former est donné par les numéros en catalan . Pour les gros n , ceux-ci se développent extrêmement rapidement. d

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top