135 votes

Pourquoi && et non &

Pourquoi est - && préférable d' & et || préférable d' |?

J'ai demandé à quelqu'un qui fait de la programmation pour les années et son explication était:

Par exemple, en if (bool1 && bool2 && bool3) { /*DoSomething*/ }, bool1 doit être vrai pour elle de tester bool2 qui doit être vrai avant de passer à l' bool3, etc. Si j'avais utilisé une seule & au lieu de cela il n'y a pas d'ordre à l'essai, même si elles ont toutes pour être vrai progrès à la ligne suivante, alors pourquoi importe-t-il de toute façon?

Note: je tiens à signaler que je suis à la programmation de l'équivalent d'un jeune enfant et ce n'est pas grave ou urgente la question, c'est plus une question de comprendre pourquoi les choses devraient être faites d'une certaine façon plutôt que d'une autre.

183voto

Daniel Hilgarth Points 90722

Dans la plupart des cas, && et || sont préférables & et | , parce que les anciens sont en court-circuit, ce qui signifie que l'évaluation est annulé dès que le résultat est clair.

Exemple:

if(CanExecute() && CanSave())
{
}

Si CanExecute retours false, l'expression complète sera false, indépendamment de la valeur de retour de l' CanSave. En raison de cette, CanSave n'est pas exécutée.

Ce qui est très pratique dans les circonstances suivantes:

string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
    // Do Something
}

TryGetValue retours false si la clé fournie n'est pas trouvé dans le dictionnaire. En raison du court-circuit de la nature de l' &&, value.Contains("test") est exécuté seulement, quand TryGetValue retours true et ainsi value n'est null. Si vous utilisez le niveau du bit ET de l'opérateur & au lieu de cela, vous obtenez un NullReferenceException si la clé ne se trouve pas dans le dictionnaire, parce que la deuxième partie de l'expression est exécutée dans tous les cas.

Un similaire mais plus simple d'exemple, le code suivant (comme mentionné par TJHeuvel):

if(op != null && op.CanExecute())
{
    // Do Something
}

CanExecute est exécuté seulement si op n'est null. Si op est null, la première partie de l'expression (op != null) correspond à la false et de l'évaluation du reste (op.CanExecute()) est ignorée.

En dehors de cela, techniquement, ils sont différents, trop:
&& et || peut être utilisée uniquement sur bool alors qu' & et | peut être utilisé sur n'importe quel type intégral (bool, int, long, sbyte, ...), parce qu'ils sont des opérateurs au niveau du bit. & est le niveau du bit ET de l'opérateur et de l' | est le bit à bit OU de l'opérateur.

Pour être très exact, en C#, les opérateurs (&, | [et ^]) sont appelés "opérateurs Logiques" (voir le C# spec, chapitre 7.11). Il existe plusieurs implémentations de ces opérateurs:

  1. Pour les entiers (int, uint, long et ulong, le chapitre 7.11.1):
    Ils sont mis en œuvre pour calculer le bit de résultat des opérandes et de l'exploitant, c'est à dire & est de mettre en œuvre pour calculer les logiques bit à bit AND etc.
  2. Pour les énumérations (chapitre 7.11.2):
    Ils sont mis en œuvre pour effectuer l'opération logique du type sous-jacent de l'énumération.
  3. Pour les booléens et nullable bool (chapitre 7.11.3 et 7.11.4):
    Le résultat n'est pas calculé à l'aide de calculs au niveau du bit. Le résultat est fondamentalement regardé sur la base des valeurs des deux opérandes, parce que le nombre de possibilités est donc faible.
    Parce que les deux valeurs sont utilisées pour la recherche, cette mise en œuvre n'est pas de court-circuit.

81voto

Jonathan Dickinson Points 4655

Pour expliquer très clairement ce que cela signifie (même si les autres réponses allusion à elle - mais probablement utiliser la terminologie que vous ne comprenez pas).

Le code suivant:

if (a && b)
{
   Foo();
}

Est vraiment compilé à ceci:

if (a)
{
    if (b)
    {
        Foo();
    }
}

D'où la suite de la compilation de code exactement comme il est représenté:

if (a & b)
{
   Foo();
}

Cela s'appelle de court-circuit. En général, vous devriez toujours utiliser && et || dans vos conditions.

Bonus de Marques: Il y a un scénario, lorsque vous ne devriez pas. Si vous êtes dans une situation où la performance est cruciale (et c'est nano-secondes essentiel) que l'utilisation de court-circuit lorsque vous devez (par exemple, null contrôle) - comme un court-circuit est une branche ou un saut, ce qui pourrait entraîner une branche-les erreurs de prédiction sur votre CPU; un & est beaucoup moins cher que d' &&. Il y a aussi un scénario de court-circuit accidentel peut réellement briser la logique - avoir un coup d'oeil à cette réponse de la mine.

Modifier/Diatribe/Monologue: en ce qui Concerne la direction de la mauvaise prédiction que la plupart des béatement ignorer. Citant Andy Firth (qui a travaillé sur les jeux pour les 13 ans): "C'est peut-être plus bas niveau que les gens pensent qu'ils ont besoin d'aller... mais ils avaient tort. La compréhension de la façon dont le matériel vous êtes à la programmation pour les friandises, les branches peuvent affecter les performances dans une large mesure,... beaucoup plus que la plupart des programmeurs peuvent apprécier re: la mort par mille coupures."

  • Les développeurs de jeux (et d'autres personnes travaillant dans l'extrême en temps réel des conditions) d'aller aussi loin que la restructuration de leur logique en fonction du prédicteur. Il existe également des preuves de cela dans décompilé mscorlib code.
  • Juste parce que .Net vous protège de ce genre de chose ne signifie pas qu'il n'est pas important. Une branche de la mauvaise prédiction est horriblement cher à 60 hz; ou à 10000 requêtes/seconde.
  • Intel n'ont pas les outils pour identifier l'emplacement d'erreurs de prédictions, ni Fenêtres ont une perf contre pour cela, il ne serait pas plus un mot pour le décrire, ce n'était pas un problème.
  • L'Ignorance au sujet d'un des niveaux les plus faibles et l'architecture ne fait pas de quelqu'un qui est conscient de la mauvaise façon.
  • Essayez toujours de comprendre les limites du matériel sur lequel vous travaillez.

Edit: Ici est une référence pour les non-croyants. Il est préférable d'exécuter le processus en temps réel/Haut pour atténuer le planificateur avoir un effet: https://gist.github.com/1200737

68voto

Lie Ryan Points 24517

opérateur logique (||,&&) vs opérateur au niveau du bit (|,&)

La différence la plus fondamentale entre la logique de l'opérateur et bit-à-bit opérateur que l'opérateur logique prend deux booléens et produit un booléen tout opérateur au niveau du bit prend deux entiers et produit un nombre entier (note: les nombres entiers s'entend de toute partie intégrante de type de données, et pas seulement de type int).

Pour être pédant, au niveau du bit opérateur prend un peu d'un motif (p. ex. 01101011) et fait un peu sage ET/OU sur chaque morceaux. Ainsi, par exemple, si vous avez deux 8-bits entiers:

a     = 00110010 (in decimal:    32+16+2   = 50)
b     = 01010011 (in decimal: 64+   16+2+1 = 83)
----------------
a & b = 00010010 (in decimal:       16+2   = 18)
a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)

tandis qu'un opérateur logique ne fonctionne que dans bool:

a      = true
b      = false
--------------
a && b = false
a || b = true

Deuxièmement, il est souvent possible d'utiliser l'opérateur au niveau du bit sur bool depuis le vrai et le faux est équivalent à 1 et 0 respectivement, et il arrive que si vous traduisez vrai 1 et false à 0, puis effectuez l'opération au niveau du bit, puis convertir les non-zéro et zéro à faux; il arrive que le résultat sera le même si vous venez d'utiliser opérateur logique (à vérifier pour l'exercice).

Une autre distinction importante est également que l'opérateur logique est court-circuitée. Ainsi, dans certains milieux, [1], vous voyez souvent les gens de faire quelque chose comme ceci:

if (person && person.punch()) { 
    person.doVictoryDance() 
}

ce qui se traduit par: if person exists (i.e. is not null), try to punch him, and if the punch succeeds (i.e. returns true), then do a victory dance

aviez-vous utilisé un opérateur au niveau du bit au lieu de cela, ceci:

if (person & person.punch()) { 
    person.doVictoryDance() 
}

traduire: if person exists (i.e. is not null) and the punch succeeds (i.e. returns true), then do a victory dance.

Notez que dans le court-circuitée opérateur logique, l' person.punch() code ne peuvent ne pas fonctionner du tout si la personne est nulle. En fait, dans ce cas particulier, le deuxième code, serait de produire une référence null erreur si la personne est nulle, puisqu'il tente de l'appeler personne.punch (), peu importe si la personne est null ou pas. Ce problème de ne pas évaluer l'opérande de droite est appelé de court-circuit.

[1] certains programmeurs vont baulk pour mettre un appel de fonction qui ont un effet secondaire à l'intérieur d'une expression, tandis que pour d'autres, c'est une commune très utile idiome.

Depuis l'opérateur au niveau du bit fonctionne sur 32-bits à la fois (si vous êtes sur une machine 32 bits), elle peut conduire à une plus élégante et la plus rapide de code si vous avez besoin de comparer un grand nombre de conditions, par exemple

int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3,
    CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6;

Person person;
person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS;

Place bar;
bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK;

Place military;
military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT;

CurrentLocation cloc1, cloc2;
cloc1.usable_abilities = person_abilities & bar_rules;
cloc2.usable_abilities = person_abilities & military_rules;

// cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT`
// while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`

faire de même avec les opérateurs logiques aurait besoin d'un maladroit nombre de comparaisons:

Person person;
person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true;
person.can_shoot_cannons = false;

Place bar;
bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true;
bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false;

Place military;
military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true;
military.rules.can_drink = military.rules.can_talk = false;

CurrentLocation cloc1;
bool cloc1.usable_abilities.can_punch         = bar.rules.can_punch         && person.can_punch, 
     cloc1.usable_abilities.can_kick          = bar.rules.can_kick          && person.can_kick, 
     cloc1.usable_abilities.can_drink         = bar.rules.can_drink         && person.can_drink, 
     cloc1.usable_abilities.can_sit           = bar.rules.can_sit           && person.can_sit, 
     cloc1.usable_abilities.can_shoot_guns    = bar.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons
     cloc1.usable_abilities.can_talk          = bar.rules.can_talk          && person.can_talk;

bool cloc2.usable_abilities.can_punch         = military.rules.can_punch         && person.can_punch, 
     cloc2.usable_abilities.can_kick          = military.rules.can_kick          && person.can_kick, 
     cloc2.usable_abilities.can_drink         = military.rules.can_drink         && person.can_drink, 
     cloc2.usable_abilities.can_sit           = military.rules.can_sit           && person.can_sit, 
     cloc2.usable_abilities.can_shoot_guns    = military.rules.can_shoot_guns    && person.can_shoot_guns,
     cloc2.usable_abilities.can_talk          = military.rules.can_talk          && person.can_talk,
     cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;

Un exemple classique où le bit-modèles et opérateur au niveau du bit sont utilisés dans les environnements Unix/Linux autorisation du système de fichiers.

8voto

fatty Points 1370

En cas de :

pourrait fonctionner comme prévu.

Mais :

peut potentiellement lever une exception de référence null.

2voto

Shrey Points 964

Courte et simple

``= true
parce que
1 = true (non nul) en c
2 = true (non nul) en c

true ANDS logiquement avec true pour donner true

mais

``= 0 = false
parce que
1 = 0001 en binaire
2 = 0010 en binaire

0001 ANDS au niveau du bit avec 0010 donner 0000 = 0 en décimal

De même pour || et | opérateurs de trop.. !!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X