Dark Sorcering in C: utilizando bit-fields

July 30, 2009

Mais um post programador-geek-útil: bit-fields. Para quem não conhece, em C pode-se criar coisas “bizarras” do tipo estruturas com campos de tamanho 1, 2 ou n bits. Sim, eu não me enganei. É isso mesmo o que tu entendeu. Em C podemos criar estruturas com campos de apenas 1 bit, por exemplo. Aliás, por sinal, tu pode até mesmo criar uma estrutura com apenas 1 bit.

“Mas como isso?” tu te pergunta. É mais fácil do que parece. Basta declarar a estrutura e, para cada campo, dizer quantos bits tu quer. Simples assim:

struct byte_t {
    unsigned char high: 4;
    unsigned char low:  4;
};

Neste caso, estamos declarando uma estrutura com dois campos, cada um com 4 bits. Viram como é simples? Neste caso, nossa estrutura terá o tamanho de 8 bits, 1 byte no final das contas. Mas nada me impede de declará-la assim:

struct bit_t {
    unsigned char val: 1;
};

Agora temos uma estrutura de apenas 1 bit de tamanho! Faça o teste: tente colocar algo mais do que 0 ou 1 no campo val da estrutura para ver se o GCC não reclama com um warning!

Algo que preciso lembrar é que o comando sizeof() não irá funcionar para a estrutura bit_t. Porque? Ora, é óbvio. A função sizeof() retorna seus valores em bytes e não em bits. Já para a estrutura byte_t, nós conseguimos executar o sizeof(), já que seu tamanho é de exatamente 1 byte.

Update: como comentado pelo meu amigo Fabio Utzig, o tamanho das estruturas em si serão arredondadas para a representação. Por exemplo, caso eu adicione mais um campo na estrutura byte_t, mesmo que seja de um bit apenas, o tamanho total da estrutura será 2 bytes, pois ele arredonda o tamanho. Os campos em si que sempre terão o tamanho em bits especificado.

3 Responses to “Dark Sorcering in C: utilizando bit-fields”

  1. Bacana isto não?!

    Usei muitas vezes na vida estruturas de bitfield. Elas especialmente interessantes pra mapear registradores de HW que são bitfields ou em protocolos de comunicação que usam campos com tamanhos arbitrários de campos. Acho que deve ser muito desagradável implementar um TCP/IP sem usar structs com bitfield já que o TCP usa vários campos com tamanhos de 5 bits e coisas assim.

    Quanto ao tamanho eu discordo do que tu disse. Uma struct com bitfields não tem o tamanho do total de bitfields e sim o arrendodamento pra cima do tamanho em bytes mais adequado que a comporte. A bit_t tem 1 byte já que o C vai usar o char pra representá-la. Se tu adicionasse mais um bit à estrutura byte_t (digamos pra sinal) ela deixaria de ter 1 byte e passaria a ter 2 bytes.

    Uma coisa que deve-se ter cuidado em bitfields é com endianess. Se tu transferir um valor pela rede de um computador com processador big endian para um little endian os bit terão de ser mexidos! Não entrarei em detalhes mas para entender isto dá pra olhar os fontes de alguns protocolos no kernel do Linux. Sempre que veres um bitfield lá vais ver um define _LITTLE_ENDIAN ou _BIG_ENDIAN (acho que é isto!).

  2. Valeu pela correção.

    Mas se tu tentares colocar mais de um bit na estrutura bit_t, dará warning. O compilador deve arredondar somente para representar a struct.

  3. Ah, sim. Entendi o que tu disseste. O campo em si terá o tamanho especificado, mas quando o compilador for representar a estrutura em memória, ele utilizará o melhor tamanho adequado.

    Aliás, o GCC tem a diretiva __attribute__ que tem a opção de tu dizer com quantos bytes tu quer representar aquela estrutura. Tipo:

    typedef struct {
    unsigned char val: 1;
    } __attribute__ ( (aligned ( 2 )) ) bit_t;

    Vai fazer com que tua estrutura seja representada com o 2 bytes. ;-)

    Valeu pela observação!

Leave a Reply