Ceci est une ancienne révision du document !
Vous avez déjà rencontrer la classe std::bitset
dans le chapitre Logique binaire et calcul booléen, pour afficher une séquence de bits.
#include <iostream> #include <bitset> int main() { const std::bitset<8> b1 { 0b101010 }; std::cout << "0b" << b1 << std::endl; const std::bitset<8> b2 { 42 }; std::cout << "0b" << b2 << std::endl; }
affiche :
0b00101010 0b00101010
Ce chapitre détaille l'utilisation de cette classe std::bitset
et les notions de flag et mask. Les notions vues dans les chapitres Logique binaire et calcul booléen et [Aller plus loin] L'algèbre de Boole seront utilisees, n'hesitez pas a les relire si nécessaire.
La classe std::bitset
est une classe template prenant en argument le nombre de bits . La taille d'un std::bitset
est donc déterminée à la compilation (Pour rappel, vous avez deja rencontré des classes qui ont des tailles determinées a la compilation ou l'exécution : respectivement std::array
et std::vector
).
Le nombre de bits geres par std::bitset
est un argument template (donc qui s'écrit entre <>
) de type entier.
std::bitset<TAILLE>
Il existe differentes methodes pour initialiser un std::bitset
:
La méthode la plus simple pour initialiser un std::bitset
est de lui fournir une valeur entière lors de l'initialisation, de préférence en utilisant une représentation binaire (avec le préfixe 0b
et les chiffres 0
et 1
) ou hexadécimale (avec le préfixe 0x
et les symboles 0
a 9
et a
a f
, en minuscule ou majuscule). Si aucune valeur n'est fournie, std::bitset
est initialisé avec la valeur nulle.
#include <bitset> int main() { const std::bitset<8> b; const std::bitset<8> b8 { 0b101010 }; const std::bitset<16> b16 { 0xA1B2 }; }
Pour faciliter la création de std::bitset
à partir d'une entrée utilisateur (flux standard, fichier, etc), il est également possible d'initialiser un std::bitset
à partir d'une chaîne de caractères. La syntaxe peut etre differente selon si vous utiliser une litterale chaine ou un type std::string
.
Le cas le plus simple est d'initialiser un std::bitset
a partir d'une chaine complete, constituee des caracteres 0
et 1
. Dans ce cas, la syntaxe est identique pour une litterale et un std::string
:
#include <bitset> #include <string> int main() { const std::bitset<8> b1 { "101010" }; const std::string s { "101010" }; const std::bitset<8> b2 { s }; }
Notez bien qu'il ne faut pas ajouter de prefixe dans la chaine de caracteres.
Un std::bitset
peut egalement etre initialise a partir d'une sous-chaine de caracteres (c'est a dire une partie d'une chaine de caracteres). La syntaxe est differentes entre une litterale et un st::string
:
std::bitset
ne peut prendre qu'un seul argument optionnel supplementaire : le nombre de caracteres a conserver ;std::string
, std::bitset
peut prendre deux arguments optionnels supplementaires : la position du premier caractere et le nombre de caracteres a conserver.Un exemple concret avec une litterale chaine :
#include <bitset> #include <iostream> int main() { const std::bitset<8> b1 { "1010101011", 4}; // les 4 premiers caracteres = "1010" std::cout << b1 << std::endl; const std::bitset<8> b2 { "1010101011", 8}; // les 8 premiers caracteres = "10101010" std::cout << b2 << std::endl; }
affiche :
00001010 10101010
Avec un std::string
:
#include <bitset> #include <string> #include <iostream> int main() { const std::string s { "1010101011" }; const std::bitset<8> b1 { s, 4 }; // commence a l'indice 4 = "101011" std::cout << b1 << std::endl; const std::bitset<8> b2 { s, 4, 2 }; // commence a l'indice 4 et conserve // 2 caracteres = "10" std::cout << b2 << std::endl; }
affiche :
00101011 00000010
N'oubliez pas qu'en C++, les indices dans les tableaux (et donc dans les chaines de caracteres, puisqu'elles peuvent etre considerees comme des tableaux de caracteres) commencent a l'indice 0. Donc l'indice 4 correspond au cinquieme caractere :
chaine : 1 0 1 0 1 0 1 0 1 1 indice : 0 1 2 3 4 5 6 7 8 9 ^
Pour terminer, il est possible d'utiliser d'autres caracteres que 0
et 1
. Pour cela, il faut fournir deux arguments supplementaires, correspondent respectivement aux caracteres a utiliser a la place de 0
et de 1
.
#include <bitset> #include <string> #include <iostream> int main() { const std::bitset<8> b1 { "BABBABBA", 8, 'A', 'B' }; std::cout << b1 << std::endl; const std::string s { "YXYYXYYX" }; const std::bitset<8> b2 { s, 0, 8, 'X', 'Y' }; std::cout << b2 << std::endl; }
affiche :
10110110 10110110
Notez que les caracteres utilisees pour representer le std::bitset
ne sont utilises que pour l'initialisation. En memoire et lors de l'affichage, un std::bitset
sera representes par defaut par une suite de 0
et 1
.
En C++, les arguments sont identifies par leur position dans l'appel d'une fonction. Par exemple pour initialiser std::bitset
(avec position
qui represente la position du premier caractere et taille
qui correspond au nombre de caracteres a conserver :
const size_t position { 2 }; const size_t taille { 4 }; const std::bitset<8> b { s, position, taille };
Cela implique qu'il n'est pas possible de changer l'ordre des arguments dans une fonction :
const std::bitset<8> b { s, taille, position };
Dans ce code, le compilateur ne va pas utiliser taille
pour le nombre de caracteres et position
pour la position du premier caractere (donc prendre 4 caracteres a partir de la position 2), mais va prendre 2 caracteres a partir de la position 4.
Pour la meme raison, si on fournit un argument optionnel, les arguments optionnels qui le precedent ne sont plus optionnels.
const std::bitset<8> b { s, taille };
Dans ce code, le compilateur ne va pas utiliser l'argument taille
fournit et considerer que l'argument position
prend sa valeur par defaut 0 (donc prendre 4 caracteres a partir de la position 0), mais va utiliser taille
comme position (donc prendre tous les caracteres apres la position 4).
Les arguments de fonction, en particulier l'ordre des arguments et les arguments optionnels seront vu en detail dans les chapitres sur la creation de fonctions.
Comme vous l'avez vu dans les codes precedents, un std::bitset
peut etre affiche directement avec std::cout
.
#include <iostream> #include <bitset> int main() { const std::bitset<8> b1 { 0b101010 }; std::cout << "0b" << b1 << std::endl; }
affiche :
0b00101010
Dans ce cas, std::bitset
sera affiche en utilisant les caracteres 0
et 1
, avec autant de caracteres que definie dans l'argument template de std::bitset
(donc en completant avec 0
si necessaire).
La fonction to_string
permet de transformer un std::bitset
en une chaine de caracteres, en utilisant par defaut les caracteres 0
et 1
(c'est l'operation inverse de l'initialisation avec une chaine). Cette fonction peut prendre deux arguments optionnels, correspondant aux caracteres a utiliser respectivement pour 0
et 1
.
La chaine de caracteres produite peut etre conservee dans une variable ou etre directement affichee avec std::cout
.
#include <iostream> #include <bitset> int main() { const std::bitset<8> b { 0b101010 }; std::cout << b.to_string() << std::endl; std::cout << b.to_string('.') << std::endl; std::cout << b.to_string('A', 'B') << std::endl; }
affiche :
00101010 ..1.1.1. AABABABA
Conceptuellement, std::bitset
est un tableau compact de booleens. Il est donc possible de manipuler directement chaque bit comme une valeur booleenne, de la lire ou de la modifier directement dans std::bitset
.
Pour connaitre la taille d'un std::bitset
(c'est a dire connaitre la valeur de l'argument template TAILLE
utilise pour initialiser le std::bitset
), vous pouvez utiliser la fonction size
.
size
pour connaitre la taille du tableau (std::vector
, std::array
, std::string
, etc). Avoir un interface coherente simplifie la memorisation et evite les erreurs. Conservez cette idee en tete lorsque vous creerez vos propres interfaces.
#include <iostream> #include <bitset> int main() { const std::bitset<8> b1{}; std::cout << b1.size() << std::endl; const std::bitset<16> b2{}; std::cout << b2 << std::endl; }
affiche :
8 16
<note>La fonction test
std::bitset
propose egalement la fonction test
pour acceder en lecture a un bit specifique. La difference avec l'operateur []
est que la fonction test
verifie si
tester un bit : mask, flag, opérateur ET bit à bit, test() forcer un bit : OU bit a bit
tester plusieurs bit : count, all, any, none (cf algo)
Plusieurs fois des données de même type. Accès avec un indice, partant de 0. Taille fixé à la compilation ou à l'exécution : bitset à la compilation (vector<bool> à l'exécution).
Validation taille : assert connaitre la taille : size
A partir de la représentation binaire d'un nombre (42 = 0b0000000000101010)