Utiliser le bitmask en C++

  1. Utiliser la struct pour définir le bitmask et implémenter l’objet date en C++
  2. Utiliser struct et union pour définir le bitmask et implémenter l’objet date en C++
  3. Utilisez std::bitset pour définir le masque de bits en C++

Cet article présente plusieurs méthodes d’utilisation du bitmask en C++.

Utiliser la struct pour définir le bitmask et implémenter l’objet date en C++

struct est une structure de données populaire en C++. De plus, elle n’est que légèrement différente de la class par opposition au langage C. Dans ce cas, il suffit de définir une struct avec plusieurs membres de données, mais il y a une nouvelle notation : utilisée après chaque nom de variable et un entier spécifié. Comme le montre l’exemple de code, nous avons déclaré une struct avec des membres de données de type uint32_t, et la somme des nombres à droite est 32. Cette construction implique que la struct occupe la taille d’un seul type de données de type uint32_t en mémoire et que l’utilisateur peut accéder à ses plages de champs de bits (23:5:4) séparément.

Cette méthode est surtout utilisée pour économiser la mémoire des structures de données qui pourraient être compressées dans des structures similaires. C’est également l’une des méthodes les plus courantes pour accéder à des données plus petites qu’un octet dans le langage de programmation C++. Notez que le premier membre year ne peut contenir que des valeurs entières jusqu’à 223-1 et les autres membres suivent de la même façon.

#include <iostream>

using std::cout;
using std::endl;

struct {
    uint32_t year:23;
    uint32_t day:5;
    uint32_t month:4;
} typedef Bitfield;

int main() {
    Bitfield date = {2020, 13, 12};

    cout << "sizeof Bitfield: " << sizeof(date) << " bytes\n";
    cout << "date: " << date.day << "/"
            << date.month << "/" << date.year << endl;

    return EXIT_SUCCESS;
}

Production :

sizeof Bitfield: 4 bytes
date: 13/12/2020

Utiliser struct et union pour définir le bitmask et implémenter l’objet date en C++

La méthode précédente est une façon suffisante et correcte de mettre en œuvre le bitmask. Elle présente néanmoins un inconvénient : l’accès aux membres et l’attribution de valeurs aux membres prennent relativement plus de temps que les opérations sur les types intégrés. Ce problème peut être résolu en implémentant un objet de type union composé d’une struct et d’une seule variable de type intégré. Notez que la structure est la même que celle de la méthode précédente. Dans ce cas, la différence est la variable uint32_t ydm qui occupe la même taille que la struct.

L’idée est d’initialiser/assigner aux membres des données leurs valeurs beaucoup plus rapidement. C’est-à-dire que nous construisons un entier avec des opérations sur les bits qui correspondent exactement à la disposition des bits lorsqu’ils sont stockés dans la struct et nous l’assignons ensuite à un seul membre uint32_t. Cela permet de gagner du temps pour accéder aux données une fois plutôt que de répéter la même opération pour chacun des trois membres.

L’arithmétique par bit est relativement simple ; à savoir, nous commençons avec la valeur du premier membre dans la struct, qui est mise en OU avec le résultat du membre suivant décalé à gauche par le nombre de bits que le membre précédent occupe, et cela suit de façon similaire.

#include <iostream>

using std::cout;
using std::endl;

union BitfieldFast{
    struct {
        uint32_t year:23;
        uint32_t day:5;
        uint32_t month:4;
    };
    uint32_t ydm;
};

int main() {
    BitfieldFast date_f{};
    date_f.ydm = 2020 | (13 << 23) | (12 << 28);

    cout << "sizeof BitfieldFast: " << sizeof(date_f) << " bytes\n";
    cout << "date: " << date_f.day << "/"
            << date_f.month << "/" << date_f.year << endl;

    return EXIT_SUCCESS;
}
sizeof BitfieldFast: 4 bytes
date: 13/12/2020

Utilisez std::bitset pour définir le masque de bits en C++

La fonction std::bitset est une fonctionnalité standard de la bibliothèque qui comprend la classe pour le stockage des données du masque binaire. Le bitset intègre de multiples fonctions de manipulation utiles, et la déclaration est assez facile. Il peut être initialisé avec une valeur binaire chaîne ou plusieurs valeurs numériques. Les opérations bitwise sont des méthodes intégrées et peuvent être appelées avec des opérateurs traditionnels qui sont surchargés. Notez que la méthode “Reset” modifie de façon permanente le bitset d’où elle est appelée.

#include <iostream>
#include <bitset>

using std::cout;
using std::endl;

int main() {
    std::bitset<16> bs1("1100100011110010");
    std::bitset<16> bs2(0xfff0);
    cout << "bs1        : " << bs1 << endl;
    cout << "bs2        : " << bs2 << endl;
    cout << "NOT bs1    : " << ~bs1 << endl;
    cout << "bs1 AND bs2: " << (bs1 & bs2) << endl;
    cout << "bs1 reset  : " << bs1.reset() << endl;

    return EXIT_SUCCESS;
}

Production :

bs1        : 1100100011110010
bs2        : 1111111111110000
NOT bs1    : 0011011100001101
bs1 AND bs2: 1100100011110000
bs1 reset  : 0000000000000000