Pourquoi cette instruction if combinant affectation et vérification d'égalité renvoie-t-elle true?

216

J'ai pensé à quelques erreurs de débutant et je me suis retrouvé avec celui sur la ifdéclaration. J'ai développé un peu le code à ceci:

int i = 0;
if (i = 1 && i == 0) {
    std::cout << i;
}

Je l' ai vu que les ifdéclarations de déclaration vrai, et il coutest icomme 1. Si iest affecté 1dans l'instruction if, pourquoi est-il i == 0revenu true?

TehMattGR
la source
83
Les gars, ce n'est pas une question de faute de frappe. L'OP veut savoir pourquoi l'instruction if est entrée avec ce code car iest défini sur 1.
NathanOliver
24
Ou attribue-t-il le résultat de 1 && i == 0?
JVApen
4
Suggestion pour les débutants: ils ne devraient pas utiliser une telle construction de langage "avancée". Attribuez simplement la variable séparément. Cela évitera également d'éventuels problèmes avec le point de séquence. Ce type de code dans le code pratique sera généralement mauvais aussi.
user202729
1
cela finira forcément par une question d'entrevue
RAZ_Muh_Taz

Réponses:

391

Cela a à voir avec la priorité des opérateurs .

if (i = 1 && i == 0)

n'est pas

if ((i = 1) && (i == 0))

parce que les deux &&et ==ont une priorité plus élevée que =. Ce que ça donne vraiment, c'est

if (i = (1 && (i == 0)))

qui affecte le résultat de 1 && (i == 0)à i. Donc, si icommence à 0alors i == 0est true, il en 1 && trueest de même true(ou 1), puis iest réglé sur 1. Puisqu'il 1est vrai, vous entrez le bloc if et imprimez la valeur que vous avez affectée i.

NathanOliver
la source
2
@ JörgWMittag C'est plutôt cool. J'aime que cela vous oblige à utiliser des parenthèses.
NathanOliver
Fondamentalement, i = !i; if (i)correctement écrit
Cacahuete Frito
@NathanOliver: Fortress était un langage assez cool qui comprenait beaucoup de choses. (Le concepteur principal était Guy L. Steele, donc pas de surprise là-bas.) Malheureusement, il n'a pas été récupéré pour le dernier tour de financement DARPA, et a ensuite été mis en veilleuse par Oracle.
Jörg W Mittag
6
Naturellement, demander un minimum d'avertissements au compilateur aurait détecté cette erreur,
Deduplicator
4
Toute langue qui ne suppose pas que les ints et les booléens sont équivalents le reprendrait également.
rghome
16

En supposant que votre code ressemble à ceci:

#include <iostream>
using namespace std;

int main()  {
    int i = 0;
    if (i = 1 && i == 0) {
        cout << i;
    }
}

Ensuite ceci:

if (i = 1 && i == 0) {

évalue comme

 if (i = (1 && i == 0)) {

et iest donc réglé sur 1.


la source
39
Le code supplémentaire était-il vraiment nécessaire? Il semble assez évident que ce serait le cas car cela ne fonctionnerait pas autrement.
Modelmat
13
Non seulement du code supplémentaire inutile. La réponse ne parvient pas à expliquer clairement la priorité de l'opérateur.
Francisco Zarabozo
34
Puisque nous sommes dans le train nitpick ... j'en vois un using namespace std!
Mateen Ulhaq
8
Il y a du code supplémentaire - mais ce n'est toujours pas un code incorrect. Et la réponse est toujours bonne. Bien sûr, cela n'explique pas la priorité des opérateurs. Mais quelqu'un pourrait suggérer qu'il soit ajouté, au lieu d'un vote purement et simplement négatif!
B Charles H
14
Wow, -4 est dur, étant donné que cela répond correctement à la question, mais peut-être pas de manière optimale. Il ne développe pas la priorité des opérateurs autant que l'autre réponse, mais il en dit juste assez à ce sujet dans le contexte du code pour que quiconque pensant que cela se =produise avant &&puisse voir le problème. De plus, oui, l'expansion est superflue, mais je ne pense pas que cela compte autant. Je ne peux pas croire que de telles différences mineures poussent les gens à voter de 151 à -4.
JoL
-4

Cela a à voir avec l'analyse des règles de droite à gauche. Par exemple, y = x + 5.
Toutes les sous-expressions sont pondérées en importance. Deux expressions d'égale importance sont évaluées de droite à gauche,. Le côté expression && est effectué en premier, suivi du LHS.

Ça a du sens pour moi.

Leslie Satenstein
la source
1
L'associativité («règles de droite à gauche») n'a rien à voir avec cela. Il s'agit de priorité ("importance"), et les opérateurs utilisés n'ont pas la même priorité.
Courses de légèreté en orbite
-4

La vraie réponse est:

  1. Le compilateur donne la priorité à "i == 0", qui a la valeur true.
  2. Ensuite, il évaluera i = 1 comme VRAI ou FAUX, et puisque les opérateurs d'affectation compilés n'échouent jamais (sinon ils ne compileraient pas), il est également évalué comme vrai.
  3. Étant donné que les deux instructions sont évaluées comme vraies et que TRUE && TRUE est évalué à TRUE, l'instruction if est évaluée à TRUE.

Pour preuve, regardez simplement la sortie asm de votre compilateur pour le code que vous avez entré (tous les commentaires sont les miens):

mov     dword ptr [rbp - 8], 0    ; i = 0;
cmp     dword ptr [rbp - 8], 0    ; i == 0?
sete    al                        ; TRUE (=1)
mov     cl, al
and     cl, 1                     ; = operator always TRUE
movzx   edx, cl
mov     dword ptr [rbp - 8], edx  ; set i=TRUE;
test    al, 1                     ; al never changed,
                                  ; so final ans is TRUE

La sortie asm ci-dessus provenait de CLANG, mais tous les autres compilateurs que j'ai examinés ont donné une sortie similaire. Cela est vrai pour tous les compilateurs sur ce site, qu'ils soient de purs compilateurs C ou C ++, tous sans pragmas pour changer le mode du compilateur (qui par défaut est C ++ pour les compilateurs C ++)

Notez que votre compilateur n'a pas réellement défini i = 1, mais i = TRUE (ce qui signifie toute valeur entière 32 bits non nulle). En effet, l'opérateur && évalue uniquement si une instruction est VRAI ou FAUX, puis définit les résultats en fonction de ce résultat. Pour preuve, essayez de changer i = 1 en i = 2 et vous pouvez constater par vous-même que rien ne changera. Voyez par vous-même à l'aide de n'importe quel compilateur en ligne dans Compiler Explorer

ar18
la source
1
1) Les documents lient à la priorité de l'opérateur C, lorsque cette question est balisée avec C ++. Deux langues différentes. 2a) i = 1est un opérateur d'assignation [et non un équivalent]; 2b) Je peux vous assurer que if (i = 0)sera évalué à une condition fausse en C et C ++, donc si cela correspond à true wrt "it never fail" est quelque peu trompeur.
TrebledJ
1
and cl, 1 ; = operator always TRUE<< corrigez-moi si je me trompe, mais je ne vois aucune affectation ici. Il représente la 1 &&partie de l'expression. Donc, cette réponse est essentiellement évaluée false.
syck
"Les documents lient à la priorité de l'opérateur C, lorsque cette question est balisée avec C ++. Deux langages différents" - et lorsque vous comparez la priorité de l'opérateur C à C ++, quelle est la différence entre les deux? Ils ont la même priorité en ce qui concerne ce sujet, ce qui n'est pas surprenant, car C ++ est un dérivé direct de C (ou une autre façon de le dire, C est un sous-ensemble du langage C ++, donc bien sûr, ils auront beaucoup en commun, y compris la priorité). Je corrigerai quand même mon message, au cas où cela prêterait à confusion.
ar18
"Corrigez-moi si je me trompe, mais je ne vois aucune affectation ici" - Alors laissez-moi vous corriger! Le 1 est une valeur immédiate et n'est le résultat d'aucun test ou calcul. C'est ce qu'on appelle une valeur "VRAIE présumée". Le seul test qui a lieu concerne l'instruction i == 0, c'est-à-dire - "cmp dword ptr [rbp - 8], 0". Vous n'auriez raison que si elle avait dit "movzx edx, 1". Selon TOUS les articles précédant le mien, il devrait y avoir deux comparaisons, mais dans la vie réelle, il n'y en a qu'un, et la sortie asm de CHAQUE compilateur majeur prouve que ces messages sont complètement incorrects.
ar18
2
En plus de vous tromper de priorité (voir la réponse de NathanOliver pour l'analyse correcte), vous faites la fausse affirmation que l'opérateur d'affectation est toujours évalué à TRUE. Essayerif ( i = 0 ) { print something } . Votre réponse se contredit également; au début, vous dites que ce qui i=1est évalué avant &&est appliqué, puis à la fin, vous dites qu'il iest défini sur le résultat de l' &&opérateur.
MM