Usar bitmask em C++

Jinku Hu 12 outubro 2023
  1. Utilizar struct para definir bitmask e implementar objecto de data em C++
  2. Utilizar struct e union para definir bitmask e implementar objecto de data em C++
  3. Utilize std::bitset para definir bitmask em C++
Usar bitmask em C++

Este artigo irá demonstrar múltiplos métodos de como utilizar a bitmask em C++.

Utilizar struct para definir bitmask e implementar objecto de data em C++

O struct é uma estrutura de dados popular em C++. Além disso, é apenas ligeiramente diferente da class em oposição à língua C. Neste caso, só precisamos de definir struct com vários membros de dados, mas há uma nova notação : utilizada após cada nome de variável e um número inteiro especificado. Como mostrado no código do exemplo, declaramos um struct com membros de dados do tipo uint32_t, e a soma dos números à direita é 32. Esta construção implica que o struct ocupa o tamanho de um único tipo de dados uint32_t na memória e o utilizador pode aceder aos seus intervalos de campo de bits(23:5:4) separadamente.

Este método é principalmente utilizado para guardar a utilização da memória de estruturas de dados que possam ser espremidas em struct semelhantes. É também uma das formas comuns de aceder a dados mais pequenos que um byte em linguagem de programação C++. Note-se que o primeiro membro year só pode conter valores inteiros até 223-1 e outros membros seguem de forma semelhante.

#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;
}

Resultado:

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

Utilizar struct e union para definir bitmask e implementar objecto de data em C++

O método anterior é uma forma suficiente e correcta de implementar a bitmask. Ainda assim, tem um lado negativo - aceder e atribuir valores aos membros leva relativamente mais tempo do que as operações em tipos incorporados. Isto pode ser resolvido através da implementação de um objecto do tipo union, que consiste num struct e numa única variável do tipo incorporado. Note-se que, o struct é a mesma disposição que construíríamos no método anterior. Neste caso, a diferença é a variável uint32_t ydm que ocupa o mesmo tamanho que o struct.

A ideia é inicializar/atribuir os seus valores aos membros dos dados muito mais rapidamente. Nomeadamente, construímos um inteiro com operações bitwise que correspondem exactamente à disposição dos bits quando estes são armazenados no struct e depois atribuímos a um único membro uint32_t. Isto poupa tempo para aceder aos dados uma vez em vez de repetir a mesma operação para cada um de três membros.

A aritmética bitwise é relativamente simples; nomeadamente, começamos com o valor do primeiro membro no struct, que é OR-ed com o resultado do membro seguinte deslocado para a esquerda pelo número de bits que o membro anterior ocupa, e segue-se de forma semelhante.

#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

Utilize std::bitset para definir bitmask em C++

std::bitset é uma característica padrão da biblioteca que inclui a classe para armazenar os dados da máscara binária. O bitset tem múltiplas funções de manipulação úteis incorporadas, e a declaração é bastante sem esforço. Pode ser inicializada com um valor binário string ou valores de múltiplos números. As operações bitwise são métodos incorporados e podem ser chamadas com operadores tradicionais que estão sobrecarregados. Note-se que o método reset modifica permanentemente o bitset a partir do qual é chamado.

#include <bitset>
#include <iostream>

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;
}

Resultado:

bs1        : 1100100011110010
bs2        : 1111111111110000
NOT bs1    : 0011011100001101
bs1 AND bs2: 1100100011110000
bs1 reset  : 0000000000000000
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook