Ir para o conteúdo

Última atualização:2026-10-12

Bit fields em C++

Sobre o autor:

Conta oficial do WeChat:

wechat

O que é um Bit field?

Um bit field (campo de bits) é uma estrutura de dados que permite armazenar dados de forma compacta em bits e permite ao programador operar sobre os bits dessa estrutura. Uma vantagem dessa estrutura de dados é que ela economiza espaço de armazenamento por unidade de dados; quando o programa precisa de milhares ou milhões de unidades de dados, esse método se torna especialmente importante. Uma segunda vantagem é que o bit field permite acessar com facilidade partes do conteúdo de um valor inteiro, simplificando o código-fonte do programa. A desvantagem dessa estrutura de dados é que a implementação de bit fields depende da máquina e do sistema concretos, podendo gerar resultados diferentes em plataformas distintas; isso torna os bit fields, em essência, não portáveis.

  • A disposição de um bit field na memória depende da máquina
  • O tipo de um bit field deve ser inteiro ou de enumeração; o comportamento de bit fields em tipos com sinal varia conforme a implementação
  • O operador de endereço (&) não pode ser aplicado a um bit field; nenhum ponteiro pode apontar para um bit field de uma classe

Uso de bit fields

Bit fields costumam ser declarados com uma estrutura; a declaração da estrutura define um nome para cada membro bit field e determina a sua largura:

struct bit_field_name
{
    type member_name : width;
};
Elemento Descrição
bit_field_name nome da estrutura de bit field
type tipo do membro do bit field, deve ser int, signed int ou unsigned int
member_name nome do membro do bit field
width número de bits ocupados pelo membro

Por exemplo, declare o seguinte bit field:

struct _PRCODE
{
    unsigned int code1: 2;
    unsigned int cdde2: 2;
    unsigned int code3: 8;
};
struct _PRCODE prcode;

Essa definição faz com que prcode contenha 2 bit fields de 2 Bits e 1 bit field de 8 Bits; podemos atribuir valores usando o operador de membro da estrutura

prcode.code1 = 0;
prcode.code2 = 3;
procde.code3 = 102;

Ao atribuir, atenção para que o valor não exceda a capacidade do membro do bit field; por exemplo, prcode.code3 é um membro de bit field de 8 Bits, cuja capacidade é 2^8 = 256, ou seja, a faixa de valores deve ser [0,255].

Tamanho e alinhamento de bit fields

O tamanho de um bit field

Por exemplo, o seguinte bit field:

struct box 
{
    unsigned int a: 1;
    unsigned int  : 3;
    unsigned int b: 4;
};

No meio dessa estrutura de bit field há um bit field sem nome, ocupando 3 Bits, que serve apenas de preenchimento, sem significado prático. O preenchimento faz a estrutura usar 8 Bits no total. Mas a linguagem C usa unsigned int como unidade básica do bit field: mesmo que o único membro de uma estrutura seja um bit field de 1 Bit, o tamanho da estrutura é o mesmo de um unsigned int. Em alguns sistemas, unsigned int tem 16 Bits; em sistemas x86, 32 Bits. O restante do artigo assume unsigned int com 32 Bits.

O alinhamento de bit fields

Um membro de bit field não pode cruzar a fronteira de dois unsigned int; se o número total de bits declarados dos membros exceder o tamanho de um unsigned int, o compilador desloca automaticamente os membros do bit field para alinhá-los segundo as fronteiras de unsigned int. Por exemplo:

struct stuff 
{
    unsigned int field1: 30;
    unsigned int field2: 4;
    unsigned int field3: 3;
};

field1 + field2 = 34 Bits, excedendo 32 Bits; o compilador desloca field2 para a próxima unidade de unsigned int, deixando uma lacuna de 2 Bits entre stuff.field1 e stuff.field2; stuff.field3 vem logo após stuff.field2, e a estrutura passa a ter tamanho de 2 * 32 = 64 Bits.

Esse vão pode ser preenchido com o membro de bit field sem nome mencionado antes; também podemos usar um membro de bit field sem nome com largura 0 para forçar o próximo membro a se alinhar com o próximo inteiro. Por exemplo:

struct stuff 
{
    unsigned int field1: 30;
    unsigned int       : 2;
    unsigned int field2: 4;
    unsigned int       : 0;
    unsigned int field3: 3; 
};

Aqui há uma lacuna de 2 Bits entre stuff.field1 e stuff.field2, e stuff.field3 é armazenado no próximo unsigned int; a estrutura passa a ter tamanho de 3 * 32 = 96 Bits.

O código para estudo está em: bit.cpp

Inicialização e remapeamento de bits de bit fields

Inicialização

A inicialização de um bit field é igual à de uma estrutura comum; listamos aqui duas formas, assim:

struct stuff s1= {20,8,6};

Ou atribua valores diretamente aos membros do bit field

struct stuff s1;
s1.field1 = 20;
s1.field2 = 8;
s1.field3 = 4;

Remapeamento de bit fields (Re-mapping)

Declare um bit field de tamanho 32 Bits

struct box {
    unsigned int ready:     2;
    unsigned int error:     2;
    unsigned int command:   4;
    unsigned int sector_no: 24;
}b1;

Zerar o bit field usando remapeamento

int* p = (int *) &b1;  // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址" 
*p = 0;                // 清除 s1,将各成员归零

Usar uma união (union) para remapear um bit field de 32 Bits para o tipo unsigned int

Primeiro, uma breve introdução às uniões

Uma "união" é uma classe especial e também uma estrutura de dados de tipo construído. Dentro de uma "união" podem ser definidos vários tipos de dados diferentes; numa variável declarada como desse tipo "união", é permitido carregar qualquer um dos dados definidos pela "união"; esses dados compartilham o mesmo trecho de memória, com o objetivo de economizar espaço.

A "união" tem algumas semelhanças com a "estrutura", mas as duas são essencialmente diferentes. Numa estrutura, cada membro tem seu próprio espaço de memória, e o comprimento total de uma variável de estrutura é a soma dos comprimentos dos membros (exceto estruturas vazias, e sem considerar ajustes de fronteira). Já na "união", os membros compartilham um trecho de memória, e o comprimento de uma variável de união é igual ao comprimento do maior dos membros. Vale esclarecer que esse compartilhamento não significa carregar vários membros simultaneamente numa variável de união, e sim que a variável de união pode receber o valor de qualquer membro, mas somente um valor por vez; atribuir um novo valor sobrescreve o antigo.

Podemos declarar a seguinte união:

union u_box {
  struct box st_box;     
  unsigned int ui_box;
};

Em sistemas x86, unsigned int e box têm ambos 32 Bits; por meio dessa união, st_box e ui_box compartilham um bloco de memória. Qual bit do bit field corresponde a qual bit do unsigned int depende do compilador e do hardware. Para zerar o bit field usando a união, o código é o seguinte:

union u_box u;
u.ui_box = 0;

Referência de estudo: http://www.yuan-ji.me/C-C-%E4%BD%8D%E5%9F%9F-Bit-fields-%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/

Tópicos relacionados

Comentários