2010-08-09 8 views
4

C'est le code que je vais utiliser pour prendre un ensemble de trois booléens et le convertir en un int pour une instruction switch:Conversion d'un ensemble de booléens à un certain nombre

int bits = 0; 
bool a = true, b = false, c = true; // 101 = 5 

bits = bits | a << 2; 
bits = bits | b << 1; 
bits = bits | c; 

cout << bits; 

J'ai huit cas sur la base sur l'état combiné de ces trois booléens. Est-ce que je fais ça bien?

Droit, pas dans le sens de la syntaxe, mais s'il y a des problèmes, s'il vous plaît aviser. Plus juste dans le sens de "Est-ce la meilleure façon de résoudre ce problème?"

+0

Voulez-vous dire C ou C++? Cela fait une différence dans la plupart des cas. –

+0

En traitant plusieurs opérateurs, j'ajoute toujours des parenthèses pour la clarté de la précédence: 'bits = bits | (a << 2); ' Cela dit, je ne suis pas sûr de savoir comment le déplacement d'un objet booléen en C++ est géré. Peut-être besoin de lancer 'a' et' b' à '(int)'. – ysap

Répondre

4

Si vous utilisez C++, vous pouvez utiliser bitset<N>.

0

Vous pouvez toujours faire la conversion explicitement:

bits = bits | (a ? 1 : 0) << 2; 

Cependant je crois que C/C++ se chargera implicitement, une fois que vous utilisez l'opérateur décalage de bits.

Vous devez définir des constantes pour chaque indicateur, ce qui donne un nom au bit que vous définissez:

const int a_flag = 2; 
bits = bits | (a ? 1 : 0) << a_flag; 

Edit:

Comme vous pouvez le voir sur les commentaires sur ma réponse, la plupart C Les programmeurs C++ préfèrent apprendre les conversions implicites et la priorité des opérateurs. Par conséquent, votre code d'origine est "le plus correct".

Edit 2:

Sauf, vous devez utiliser l'opérateur |=:

const int a_flag = 2; 
bits |= a << a_flag; 
+0

En C++, 'bool' est un type entier qui a des valeurs 0 ou 1, donc être explicite est inutile. (Et l'utilisation de l'opérateur ternaire aurait pour effet d'éviter l'utilisation de 'switch' au lieu d'une séquence d'instructions' if'.) – jamesdlin

+0

'(a? 1: 0)' signifie '(a! = 0)' – wilhelmtell

+0

@wilhelmtell : Ce que "signifie" est ce que le code dit. Le sens est défini en termes de ce que l'humain lit. Ce qui est logiquement substituable ne décrit pas vraiment l'intention du programmeur. En outre, quel code compile à est une histoire différente :) Le code compilé n'a pas un concept de booléens, juste des valeurs entières contraintes. –

1

Vous faites bien. Vous pouvez rendre le code un peu plus succinct que:

bits |= (a<<2) | (b<<1) | (c<<0); 

Soyez conscient que la norme n'impose aucune contrainte de taille sur bool. Pragmatiquement parlant, il ne devrait pas y avoir de problème avec trois bits, mais la norme ne vous soutient pas ici.

+0

Pourquoi une contrainte de taille sur 'bool' est-elle importante? 'b << i' devrait aboutir à un entier. –

+0

'b << i' donne un type entier du type de l'opérande gauche. Voir 5.8/1. – wilhelmtell

0

je le ferais comme:

bits = (bits << 1) | a; 
bits = (bits << 1) | b; 
bits = (bits << 1) | c; 

qui nécessiterait moins de travail d'entretien si vous avez besoin d'ajouter ou de supprimer un drapeau. Toutefois, faire ceci pour que vous puissiez l'utiliser pour un switch semble être une mauvaise idée. L'ajout d'un drapeau doublerait le nombre d'états que vous auriez à gérer, et les valeurs case seraient fragiles et difficiles à lire.

Mais si vous devez vraiment, voici une autre approche:

enum 
{ 
    c_bit_offset, 
    b_bit_offset, 
    a_bit_offset 
}; 

unsigned int bits = (a << a_bit_offset) 
        | (b << b_bit_offset) 
        | (c << c_bit_offset); 
switch (bits) 
{ 
    case 0: 
     /* Do something. */ 
     break; 
    case (1 << a_bit_offset): 
     /* Do something. */ 
     break; 
    case (1 << a_bit_offset) | (1 << b_bit_offset): 
     /* Do something. */ 
     break; 
    ... 
} 

Par ailleurs, vous devriez probablement utiliser unsigned int au lieu de int.

1

$ cat ttt.c

//example of C solution 
#include <stdio.h> 

int main() { 
     union { 
       unsigned int val; 
       struct { 
         unsigned a : 1; 
         unsigned b : 1; 
         unsigned c : 1; 
         //unsigned d : 1; 
         //e, f, g, h... 
       } flags; 
     } bits; 

     bits.val=0; 
     bits.flags.a = 1; 
     bits.flags.c = 1; 

     printf("val: %d\n",bits.val); 
     return 0; 
} 

~ ./ttt $

val: 5 
+1

Techniquement, écrire une valeur d'un 'union' puis en lire un autre appelle un comportement indéfini. – sbi

+0

Super! J'avais oublié comment les syndicats peuvent rendre les opérations de bits si faciles! Juste une question, est-ce que les syndicats augmentent la taille du code par rapport à l'option "define"? – RMAAlmeida

+1

Moi aussi, j'ai été la proie des syndicats. En Russie soviétique, ils disent "vous n'utilisez pas l'Union, l'Union vous utilise". Pour des raisons d'efficacité, le standard C++ permet aux implémenteurs de compilateurs d'aligner les structures et les unions comme bon leur semble. Ceci, au détriment de nous permettre de simpletons de compter sur un membre se trouvant dans une adresse spécifique en mémoire. Ce que cela implique dans le cas des syndicats, comme le note correctement @sbi, c'est que vous ne pouvez pas écrire dans un membre et ensuite lire dans un autre. L'adresse de départ du membre que vous avez écrit n'est peut-être pas ce que vous attendez. Hélas, ce programme est incorrect. – wilhelmtell

0

Vous pouvez faire pour faire quelques définit facile de travailler avec des morceaux

#define BitSet(arg,bit) ((arg) |= (1<<bit)) 
#define BitClr(arg,bit) ((arg) &= ~(1<<bit)) 
#define BitFlp(arg,bit) ((arg) ^= (1<<bit)) 
#define BitTst(arg,bit) ((arg) & (1<<bit)) 

Ensuite, vous pouvez utiliser un seul omble chevalier

keys = 0b00000101; 
BitSet (keys,1); 

Ceci est une façon courante de travailler dans les systèmes embarqués tems.

Questions connexes