web-dev-qa-db-fra.com

Comment basculer un int/_Bool en C

Supposons que nous ayons une int et que nous voulions basculer entre 0 et 1 de manière booléenne. J'ai pensé aux possibilités suivantes:

int value = 0; // May as well be 1

value = value == 0 ? 1 : 0;
value = (value + 1) % 2;
value = !value; // I was curious if that would do...
  1. Le troisième semble fonctionner. Pourquoi? Qui décide que !0 est 1?
  2. Quelque chose ne va pas avec l'un de ces?
  3. Y a-t-il d'autres possibilités? par exemple. opérateurs au niveau des bits?
  4. Laquelle offre la meilleure performance?
  5. Tout cela serait-il identique à _Bool (ou bool de stdbool.h)? Si non, quelles sont les différences?

EDIT: Beaucoup de bonnes réponses avec beaucoup d'informations précieuses, merci! Malheureusement, je ne peux en accepter qu'un.

13
riha

value = !value; exprime ce que vous voulez faire directement, et il fait exactement ce que vous voulez faire par définition.

Utilisez cette expression.

A partir de C99 6.5.3.3/5 "Opérateurs arithmétiques unaires":

Le résultat de l'opérateur de négation logique! vaut 0 si la valeur de son opérande est différente de 0, 1 si la valeur de son opérande compare à 0. Le résultat est de type int. L'expression! E est équivalente À (0 == E).

25
Michael Burr

Le troisième semble fonctionner. Pourquoi? Qui décide que! 0 est 1?

C Standard garantit que !0 est 1.

Y a-t-il d'autres possibilités? par exemple. opérateurs au niveau des bits?

Oui, vous pouvez utiliser l'opérateur exclusif OR:

value ^= 1;

En passant, je préfère ceci à value = !value; car les opérateurs relationnels et d’égalité peuvent entraîner des branches et les opérateurs au niveau des bits ne le font généralement pas. 

11
ouah

value = !value semble le plus raisonnable, mais vous pouvez également utiliser value = 1 - value ou value ^= 1. Mais les deux derniers casse si value n'est pas 0 ou 1. Le premier fonctionnerait toujours.

3
detunized
  1. la langue a été conçue de cette façon.
  2. Utilisez le troisième, les autres sont justes mais inutilement compliqués et cachent donc l’intention.
  3. value = (value ^ 1) & 1;
  4. Ils sont tous identiques après optimisation. 
  5. _Bool aurait les mêmes résultats. La seule différence avec _Bool est que les valeurs sont forcées d'être 1 ou 0. Cela signifie que bool x = 55; aura la valeur x == 1

EDIT: Correction de la formule en 3 à cause de mon brainfart. Je laisse les commentaires pour que les gens puissent voir mon bêtisier.

3
Patrick Schlüter

Il peut y avoir des problèmes de performances notables avec les alternatives en fonction de l'architecture:

  • ! a pourrait avoir besoin dans certaines architectures de comparaison et de branchement, ce qui peut coûter cher en fonction du modèle de 'a'
    • sur certaines architectures, il y a un mouvement conditionnel (qui est sans branche), mais
      qui peut nécessiter encore 3 instructions à remplir (avec dépendances)
  • 1-a très probablement besoin de deux instructions dans plusieurs architectures
    • exemple de compteur: ARM a une soustraction inverseRSB% r0,% r0, # 1
  • 1 ^ a peut être implémenté dans de nombreuses architectures avec une seule instruction
  • a = (a + 1)% 2 sera probablement optimisé à a = (a + 1) & 1, ce qui nécessite 2 instructions

Quoi qu'il en soit, la première règle d'optimisation est de ne pas optimiser un code qui ne fonctionne pas. Pour remplacer! A par un ^ 1, il faut être sûr à 100% qu'il produit toujours la valeur attendue.

3
Aki Suihkonen

Votre expression value = value == 0 ? 1 : 0; fonctionnera exactement comme value = !value;. Vous pouvez utiliser n'importe lequel des deux.

!0 est toujours 1 et !(any non zero value) est également 0

1
CCoder
value = 1 - value; // toggle from 0 to 1 ... or 1 to 0
                   // when you know initial value is either 0 or 1
1
Scott Stensland

En C #, vous pouvez utiliser Math.Abs(value -1) pour basculer entre zéro et un en tant qu'entiers.

0
tkerwood