Les opérateurs logiques de court-circuit sont-ils obligatoires? Et ordre d’évaluation?

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

Je suis confus car je me souviens du livre de K & R disant que votre code ne devrait pas dépendre du court-circuit de ces opérations, car ce n’est peut-être pas le cas. Quelqu’un pourrait-il s’il vous plaît indiquer où dans la norme, il est dit que les opérations logiques sont toujours court-circuitées? Je suis surtout intéressé par le C ++, une réponse aussi pour C serait géniale.

Je me souviens aussi d’avoir lu (ne me souviens plus où) que l’ordre d’évaluation n’est pas ssortingctement défini, donc votre code ne devrait pas dépendre ou assumer des fonctions dans une expression spécifique: aura été appelé, mais le compilateur a la liberté de choisir l’ordre le plus efficace.

La norme indique-t-elle l’ordre d’évaluation de cette expression?

if( functionA() && functionB() && functionC() ) cout<<"Hello world"; 

Oui, un court-circuit et un ordre d’évaluation sont requirejs pour les opérateurs || et && dans les deux 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 

en utilisant la signification intégrée des opérateurs dans ces expressions, il y a un sharepoint séquence après l’évaluation de la première expression (12).

En C ++, il y a un piège supplémentaire: le court-circuit ne s’applique PAS aux types qui surchargent les opérateurs || et && .

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

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

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

Si ce n’était pas le cas, le 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 } 

La section 6.5.13 Opérateur logique ET de la spécification C99 (lien PDF) indique

(4). Contrairement au binary et à l’opérateur binary, l’opérateur && garantit une évaluation de gauche à droite; il y a un sharepoint séquence après l’évaluation du premier opérande. Si le premier opérande est égal à 0, le deuxième opérande n’est pas évalué.

De même, la section 6.5.14 Opérateur logique indique

(4) contrairement au bitwise | opérateur, le || l’opérateur garantit une évaluation de gauche à droite; il y a un sharepoint séquence après l’évaluation du premier opérande. Si le premier opérande est différent de 0, le deuxième opérande n’est pas évalué.

Des termes similaires peuvent être trouvés dans les normes C ++, consultez la section 5.14 de ce brouillon . Comme les vérificateurs le notent dans une autre réponse, si vous écrasez && ou ||, alors les deux opérandes doivent être évalués car ils deviennent un appel de fonction normal.

Oui, cela le prescrit (à la fois ordre d’évaluation et court-circuit). Dans votre exemple, si toutes les fonctions renvoient true, l’ordre des appels provient ssortingctement de functionA puis functionB puis functionC. 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 l’opérande gauche et droit de && , || , et entre le premier et le deuxième / troisième opérande de ?: (opérateur conditionnel) est un “sharepoint 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 choses:

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

Le standard C ++ indique en 5.14/1 :

Les 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 à &, && garantit une évaluation de gauche à droite: le second opérande n’est pas évalué si le premier opérande est faux.

Et en 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 vrai, 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é à true.

Il dit pour les deux à côté de ceux:

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

En plus de cela, 1.9/18 dit

Dans l’évaluation de chacune des expressions

  • a && 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 y a un sharepoint séquence après l’évaluation de la première expression.

Directement du bon vieux K & R:

C garantit que && 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 POD, ce sont des opérateurs de raccourci.

Mais si vous définissez ces opérateurs pour vos propres classes, 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 && et l’opérateur || pour les types de POD, l’ordre d’évaluation est de gauche à droite (sinon, un raccourcissement serait difficile 🙂 Mais pour les opérateurs surchargés que vous définissez, il s’agit essentiellement de sucre syntaxique pour définir une méthode.

Si vous avez confiance en Wikipedia:

[ && et || ] sont sémantiquement distincts des opérateurs de bits et & | parce qu’ils ne seront jamais évaluer le bon opérande si le résultat peut être déterminé à partir de la gauche seul

http://en.wikipedia.org/wiki/C_(programming_language)#Characteristics

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

Pour la préséance, lorsque vous avez quelque chose comme A op1 B op2 C , vous pouvez regrouper les choses comme (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 second.

Pour l’associativité, lorsque vous avez quelque chose comme A op B op C , vous pouvez encore regrouper les sous forme de (A op B) op C ou A op (B op C) . Si op a quitté l’associativité, on se retrouve avec la première expression. S’il y a une bonne associativité, on se retrouve avec le second. Cela fonctionne également pour les opérateurs au même niveau de priorité.

Dans ce cas particulier, && a une plus grande priorité que || , donc l’expression sera évaluée comme (a != "" && it == seqMap.end()) || isEven (a != "" && it == seqMap.end()) || isEven

L’ordre lui-même est “de gauche à droite” sur la forme de l’arbre d’expression. Nous allons donc d’abord évaluer a != "" && it == seqMap.end() . Si c’est vrai, l’expression entière est vraie, sinon nous allons à isEven . La procédure se répète récursivement à l’intérieur de la sous-expression de gauche, bien sûr.


Informations intéressantes, mais le concept de priorité trouve 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 équation A1 op1 A2 op2 ... opn-1 An , où tous les opérateurs ont la même priorité, le nombre d’arbres d’expression binary que nous pourrions former est donné par les nombres catalans . Pour les gros n , ceux-ci se développent extrêmement rapidement. ré