C Campos de Bits

Muitas informações de um programa são pequenas: "o despertador está ligado?" (sim ou não), "que dia da semana?" (1 a 7), "que hora?" (0 a 23). Guardar cada uma num unsigned inteiro, que tem 32 bits, é como usar uma mala grande para levar um par de meias. Os campos de bits (bit-fields) deixam você dizer ao C quantos bits cada membro de uma struct precisa, e ele encaixa vários membros no mesmo espaço.

Você vai aprender
  • A sintaxe unsigned ativo : 1; e o que o número depois dos dois-pontos quer dizer.
  • Quantos valores cabem em 1, 2, 3… bits, e como escolher a largura.
  • Quanto espaço se economiza, medido com sizeof.
  • O que acontece quando você guarda um valor que não cabe (e o aviso do compilador).
  • A pegadinha do int x : 1, que guarda -1 em vez de 1.
  • Os limites: não dá para pegar o endereço de um campo (adeus, scanf direto), nem usar sizeof nele, e a ordem dos bits na memória depende do compilador.

Esta aula usa structs (veja Structs) e fala de bits, os zeros e uns que formam cada número (a aula Memória, Bits e Bytes explica). Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.


A ideia: uma gaveta com divisórias

Imagine uma gaveta de cozinha. Você pode jogar tudo lá dentro, ou pôr divisórias: um espaço estreito para os palitos, um médio para as colheres, um largo para as conchas. A gaveta é a mesma; as divisórias só aproveitam melhor o espaço.

Um unsigned é a gaveta: tem 32 bits. Um campo de bits é uma divisória: você diz "este membro usa 1 bit, este usa 5, este usa 6", e o compilador coloca todos, lado a lado, dentro da mesma gaveta de 32 bits.

Informação do despertadorValores possíveisBits que bastam
Está ligado?0 ou 11 bit
Hora0 a 235 bits (cabem 0 a 31)
Minuto0 a 596 bits (cabem 0 a 63)
Dia da semana1 a 73 bits (cabem 0 a 7)
Total15 bits: cabe numa gaveta só

1. O problema: uma struct "folgada"

Primeiro, o despertador da Ana do jeito que você já sabe fazer, um unsigned para cada informação:

#include <stdio.h>

struct Despertador {
    unsigned ativo;
    unsigned hora;
    unsigned minuto;
    unsigned dia;
};

int main(void) {
    struct Despertador d = {1, 6, 30, 2};
    printf("Toca às %02u:%02u no dia %u (ativo: %u)\n", d.hora, d.minuto, d.dia, d.ativo);
    printf("sizeof: %zu bytes\n", sizeof d);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra semcampos.c -o semcampos
$ ./semcampos
Toca às 06:30 no dia 2 (ativo: 1)
sizeof: 16 bytes

Quatro membros de 4 bytes cada: 16 bytes, ou 128 bits, para guardar 15 bits de informação. O %02u mostra o número sem sinal com dois dígitos, completando com zero à esquerda (veja Formatos do printf); o sizeof mede o tamanho em bytes (aula sizeof).


2. A sintaxe: dois-pontos e a largura

Agora o mesmo despertador com campos de bits. A única mudança está dentro da struct:

#include <stdio.h>

struct Despertador {
    unsigned ativo  : 1;
    unsigned hora   : 5;
    unsigned minuto : 6;
    unsigned dia    : 3;
};

int main(void) {
    struct Despertador d = {1, 6, 30, 2};
    printf("Toca às %02u:%02u no dia %u (ativo: %u)\n", d.hora, d.minuto, d.dia, d.ativo);
    printf("sizeof: %zu bytes\n", sizeof d);
    d.hora = 23;
    d.minuto = 59;
    d.ativo = 0;
    printf("Agora: %02u:%02u (ativo: %u)\n", d.hora, d.minuto, d.ativo);
    return 0;
}
unsigned
O tipo do campo: inteiro sem sinal (só zero e positivos). É o tipo mais usado em campos de bits, e a seção 5 mostra por quê.
ativo
O nome do membro, como em qualquer struct.
: 1
A largura do campo, em bits. Leia "ativo ocupa 1 bit". Os espaços em volta dos dois-pontos são opcionais; aqui eles só alinham a coluna.
hora : 5
5 bits guardam de 0 a 31: sobra espaço para 0 a 23.
d.hora = 23;
Fora da struct, nada muda: você lê e escreve o campo com o ponto, como um membro comum.
{1, 6, 30, 2}
A inicialização também é igual: os valores vão na ordem dos membros.
$ gcc -std=c23 -Wall -Wextra campos.c -o campos
$ ./campos
Toca às 06:30 no dia 2 (ativo: 1)
sizeof: 4 bytes          « eram 16
Agora: 23:59 (ativo: 0)

Mesmo comportamento, um quarto do espaço. Os quatro campos somam 15 bits e couberam numa única "gaveta" de 4 bytes (32 bits); os 17 bits que sobram ficam sem uso.

Experimente: rode e veja o sizeof. Depois acrescente um campo unsigned segundo : 6;: o tamanho muda? (São 21 bits: ainda cabe em 32.) Agora acrescente unsigned ano : 12; (33 bits no total) e veja o que acontece com o sizeof.

#include <stdio.h>

struct Despertador {
    unsigned ativo  : 1;
    unsigned hora   : 5;
    unsigned minuto : 6;
    unsigned dia    : 3;
};

int main(void) {
    struct Despertador d = {1, 6, 30, 2};
    printf("Toca às %02u:%02u no dia %u (ativo: %u)\n", d.hora, d.minuto, d.dia, d.ativo);
    printf("sizeof: %zu bytes\n", sizeof d);
    return 0;
}

Quando os bits passam de 32, o compilador abre uma segunda gaveta e o sizeof pula para 8. O tamanho cresce de 4 em 4 bytes, porque cada "gaveta" é um unsigned inteiro.


3. Quanto cabe em cada largura

Cada bit a mais dobra a quantidade de valores. Com n bits sem sinal, cabem os números de 0 até 2n − 1:

LarguraValores (unsigned)Serve para…
: 10 a 1sim/não, ligado/desligado
: 20 a 34 opções (P, M, G, GG)
: 30 a 7dia da semana
: 40 a 15mês (1 a 12)
: 50 a 31dia do mês, hora
: 60 a 63minuto, segundo
: 80 a 255um byte inteiro (uma cor RGB)
: 120 a 4095ano

A regra para escolher: ache o maior valor que o campo vai guardar e pegue a menor largura em que ele cabe. Para o mês (até 12): 3 bits vão até 7 (não dá); 4 bits vão até 15 (dá).


4. Quando o valor não cabe

O campo nivel abaixo tem 3 bits (0 a 7). O que acontece se a Ana tentar guardar 9?

#include <stdio.h>

struct Volume {
    unsigned nivel : 3;
};

int main(void) {
    struct Volume v = {0};
    v.nivel = 7;
    printf("nível 7 -> %u\n", v.nivel);
    v.nivel = 9;
    printf("nível 9 -> %u\n", v.nivel);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra estouro.c -o estouro
estouro.c: In function ‘main’:
estouro.c:11:15: warning: unsigned conversion from ‘int’ to ‘unsigned char:3’ changes value from ‘9’ to ‘1’ [-Woverflow]
   11 |     v.nivel = 9;
      |               ^
$ ./estouro
nível 7 -> 7
nível 9 -> 1             « o 9 virou 1
unsigned conversion from ‘int’ to ‘unsigned char:3’
"Conversão sem sinal de int para um campo de 3 bits". O gcc descreve o campo como unsigned char:3: o tipo pequeno que ele usou por dentro e a largura.
changes value from ‘9’ to ‘1’
"Muda o valor de 9 para 1". O compilador já fez a conta e avisa o resultado.
[-Woverflow]
A regra do aviso: overflow é "transbordar". Ela vem ligada mesmo sem -Wall.

Por que 1? Em binário, 9 é 1001. O campo só tem lugar para os 3 bits da direita (001); o bit que sobra à esquerda é jogado fora. É como um hodômetro de carro com 3 dígitos: depois de 999 ele volta para 000. Em conta: o valor guardado é o resto da divisão por 8 (9 % 8 = 1).

O Clang, compilador do editor, avisa com outras palavras: implicit truncation from 'int' to bit-field changes value from 9 to 1 [-Wbitfield-constant-conversion] ("truncamento implícito", isto é, corte escondido).

Com uma variável, nenhum aviso

O compilador só avisa quando o número está escrito direto no código. Se o valor vem de uma variável, o corte acontece em silêncio:

#include <stdio.h>

struct Volume {
    unsigned nivel : 3;
};

int main(void) {
    struct Volume v = {0};
    for (unsigned i = 5; i <= 10; i++) {
        v.nivel = i;
        printf("guardei %2u, ficou %u\n", i, v.nivel);
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra estouro2.c -o estouro2     « nenhum aviso
$ ./estouro2
guardei  5, ficou 5
guardei  6, ficou 6
guardei  7, ficou 7
guardei  8, ficou 0      « deu a volta
guardei  9, ficou 1
guardei 10, ficou 2

Para campos unsigned, esse "dar a volta" é garantido pela linguagem: não é erro, é aritmética de relógio. Mas quase nunca é o que você queria. Por isso, confira o valor antes de guardar num campo pequeno (if (i <= 7)) ou dê ao campo a largura certa.

Missão: o volume da caixinha de som da Ana agora vai até 10, mas o campo nivel só guarda até 7. Mude só a largura do campo para que a última linha mostre guardei 10, ficou 10.

#include <stdio.h>

struct Volume {
    unsigned nivel : 3;
};

int main(void) {
    struct Volume v = {0};
    for (unsigned i = 5; i <= 10; i++) {
        v.nivel = i;
        printf("guardei %2u, ficou %u\n", i, v.nivel);
    }
    return 0;
}
#include <stdio.h>

struct Volume {
    unsigned nivel : 4;
};

int main(void) {
    struct Volume v = {0};
    for (unsigned i = 5; i <= 10; i++) {
        v.nivel = i;
        printf("guardei %2u, ficou %u\n", i, v.nivel);
    }
    return 0;
}

5. A pegadinha do int x : 1

E se o campo for int (com sinal) em vez de unsigned? Com sinal, um dos bits serve para dizer se o número é negativo. Num campo de 1 bit, esse bit é o único que existe:

#include <stdio.h>

struct Flags {
    int ligado : 1;
    bool aceso : 1;
};

int main(void) {
    struct Flags f = {0};
    f.ligado = 1;
    f.aceso = 1;
    printf("ligado = %d\n", f.ligado);
    printf("aceso  = %d\n", f.aceso);
    if (f.ligado == 1) printf("ligado é 1\n");
    else printf("ligado NÃO é 1\n");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra sinal.c -o sinal
sinal.c: In function ‘main’:
sinal.c:14:18: warning: comparison is always false due to limited range of data type [-Wtype-limits]
   14 |     if (f.ligado == 1) printf("ligado é 1\n");
      |                  ^~
$ ./sinal
ligado = -1              « guardei 1, saiu -1
aceso  = 1
ligado NÃO é 1
  • Um campo int de 1 bit só guarda dois valores: 0 e −1. O 1 não existe nele.
  • O gcc percebeu na comparação: comparison is always false due to limited range of data type = "a comparação é sempre falsa por causa do alcance limitado do tipo".
  • O Clang (editor) avisa já na atribuição: implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1.
  • O campo bool aceso : 1 funciona como esperado: guarda 0 ou 1. Em C23, bool é palavra-chave C23; no C99 a C17, escreva #include <stdbool.h> (veja bool).
Regra prática: campo de bits é unsigned (ou bool)

Use int com sinal só se o campo precisa mesmo guardar negativos, e com largura suficiente (um int x : 4 guarda de −8 a 7). E nunca escreva só int esperando sem sinal: num campo de bits, se o int "puro" tem sinal ou não é uma escolha do compilador. Escreva signed int ou unsigned int para não deixar dúvida.


6. Que tipos podem ter campos de bits

Tipo do campoO padrão C garante?Observação
unsigned int (ou só unsigned)SimO mais comum. Largura de 1 a 32 no gcc e no editor.
signed intSimGuarda negativos; cuidado com largura 1.
intSimCom sinal ou não: depende do compilador. Evite.
boolSim C99Largura 1. Ótimo para sim/não.
unsigned char, uint8_t, unsigned long…Não (cada compilador decide)O gcc e o Clang aceitam; um compilador mais antigo pode recusar.

O tipo também define a largura máxima. Um campo não pode ser mais largo que o próprio tipo:

#include <stdio.h>

struct Errado {
    unsigned grande : 40;
};

int main(void) {
    struct Errado e = {0};
    printf("%zu\n", sizeof e);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra largura.c -o largura
largura.c:4:14: error: width of ‘grande’ exceeds its type
    4 |     unsigned grande : 40;
      |              ^~~~~~

Width of ‘grande’ exceeds its type = "a largura de grande passa do tamanho do tipo". Um unsigned tem 32 bits. O Clang é mais detalhado: width of bit-field 'grande' (40 bits) exceeds the width of its type (32 bits). Para 40 bits, use um tipo maior, como uint64_t (aula Inteiros de Tamanho Fixo), que o gcc e o Clang aceitam.


7. O que você não pode fazer com um campo

O menor pedaço de memória que tem endereço é o byte. Um campo de bits pode começar no meio de um byte, então ele não tem endereço próprio. Isso traz três consequências.

Não dá para usar & (nem scanf direto)

#include <stdio.h>

struct Despertador {
    unsigned ativo  : 1;
    unsigned hora   : 5;
};

int main(void) {
    struct Despertador d = {1, 6};
    printf("Nova hora: ");
    scanf("%u", &d.hora);
    printf("Hora: %u\n", d.hora);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra endereco.c -o endereco
endereco.c: In function ‘main’:
endereco.c:11:17: error: cannot take address of bit-field ‘hora’
   11 |     scanf("%u", &d.hora);
      |                 ^

Cannot take address of bit-field ‘hora’ = "não é possível pegar o endereço do campo de bits hora". O scanf precisa de um endereço para saber onde guardar (aula scanf), e o campo não tem um. No Clang: address of bit-field requested ("foi pedido o endereço de um campo de bits"). Pelo mesmo motivo, também não existe ponteiro para um campo (unsigned *p = &d.hora; dá o mesmo erro).

O conserto: leia numa variável comum e depois copie para o campo. De quebra, você confere o valor antes, e o problema da seção 4 some.

Missão: este programa não compila. Crie uma variável unsigned h = 0;, leia nela com o scanf e, se ela for no máximo 23, guarde em d.hora. A entrada já tem o número 7; o programa tem que mostrar Hora: 7.

#include <stdio.h>

struct Despertador {
    unsigned ativo  : 1;
    unsigned hora   : 5;
};

int main(void) {
    struct Despertador d = {1, 6};
    printf("Nova hora: ");
    scanf("%u", &d.hora);
    printf("Hora: %u\n", d.hora);
    return 0;
}
#include <stdio.h>

struct Despertador {
    unsigned ativo  : 1;
    unsigned hora   : 5;
};

int main(void) {
    struct Despertador d = {1, 6};
    unsigned h = 0;
    printf("Nova hora: ");
    if (scanf("%u", &h) == 1 && h <= 23) {
        d.hora = h;
    }
    printf("Hora: %u\n", d.hora);
    return 0;
}

Não dá para usar sizeof num campo

O sizeof mede em bytes, e um campo de 5 bits não tem um número inteiro de bytes:

$ cat sizeofcampo.c
#include <stdio.h>

struct D { unsigned hora : 5; };

int main(void) {
    struct D d = {6};
    printf("%zu\n", sizeof d.hora);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra sizeofcampo.c -o sizeofcampo
sizeofcampo.c: In function ‘main’:
sizeofcampo.c:7:28: error: ‘sizeof’ applied to a bit-field
    7 |     printf("%zu\n", sizeof d.hora);
      |                            ^

"sizeof aplicado a um campo de bits". O sizeof d, da struct inteira, continua valendo.

Não existe array de campos de bits

unsigned luzes[8] : 1; não existe em C. Se a Ana quer 8 interruptores, ela escreve 8 campos com nomes (sala, cozinha…) ou usa um número comum e os operadores de bits (aulas Operadores de Bits e Máscaras e Flags), que permitem escolher o bit por um índice.


8. Onde cada campo fica: depende do compilador

Dentro da "gaveta", qual campo fica nos bits da direita e qual fica nos da esquerda? O padrão C não diz: cada compilador (e cada tipo de processador) escolhe. Este programa copia os bytes da struct para um unsigned comum com memcpy (aula Funções de String) e mostra o número em hexadecimal:

#include <stdio.h>
#include <string.h>

struct Tres {
    unsigned a : 4;
    unsigned b : 4;
    unsigned c : 8;
};

int main(void) {
    struct Tres t = {0};
    t.a = 0x1;
    t.b = 0x2;
    t.c = 0xFF;
    unsigned bruto = 0;
    memcpy(&bruto, &t, sizeof bruto);
    printf("bruto = 0x%08X\n", bruto);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra ordem.c -o ordem
$ ./ordem
bruto = 0x0000FF21

Lendo da direita para a esquerda (cada dígito hexadecimal são 4 bits): 1 é o a, 2 é o b, FF é o c. No gcc do laboratório e no Clang do editor, o primeiro campo fica nos bits mais baixos (os da direita), e os dois mostram 0x0000FF21. Em outros processadores e compiladores, a ordem pode ser a contrária, ou o compilador pode deixar espaços entre os campos.

Não use campos de bits para formatos "de fora"

Se os bits vão sair do programa (gravados num arquivo, enviados pela rede, escritos num registrador de um aparelho), a posição exata importa, e com campos de bits ela muda de compilador para compilador. Nesses casos, use um inteiro de tamanho fixo (uint32_t) e monte os bits você mesmo com <<, | e & (aula Máscaras e Flags). Campos de bits são ótimos para dados que ficam dentro do programa.

Campos sem nome e largura zero

Dois recursos menos usados, para quando você quer controlar o espaço:

#include <stdio.h>

struct A { unsigned x : 4; unsigned y : 4; };
struct B { unsigned x : 4; unsigned : 0; unsigned y : 4; };
struct C { unsigned x : 4; unsigned : 4; unsigned y : 4; };

int main(void) {
    printf("A=%zu B=%zu C=%zu\n", sizeof(struct A), sizeof(struct B), sizeof(struct C));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra zero.c -o zero
$ ./zero
A=4 B=8 C=4
unsigned : 0;
Campo sem nome e de largura zero: "feche esta gaveta; o próximo campo começa numa nova". Por isso B tem 8 bytes.
unsigned : 4;
Campo sem nome de 4 bits: um "espaço reservado" que ninguém usa, só para empurrar o y para a frente. C continua com 4 bytes (12 bits no total).

9. Vale a pena? Formas diferentes de guardar sim/não

Economizar 12 bytes num despertador não muda nada. Campos de bits fazem diferença quando há muitos registros: um milhão de despertadores ocupam 16 MB sem campos e 4 MB com eles. Mas cada leitura de um campo custa algumas instruções a mais (o processador precisa "recortar" os bits), e há os limites que você viu.

FormaEspaçoPrósContras
Membros comuns (unsigned ativo;, bool ativo;)MaiorSimples; tem endereço; scanf diretoDesperdiça memória
Campos de bits (unsigned ativo : 1;)PequenoUsa como membro comum, com nomeSem &; ordem dos bits não é portátil; corte silencioso
Um inteiro com máscaras (uint8_t flags; e flags & ATIVO)PequenoPosição exata dos bits; combina várias flags numa contaCódigo menos legível; exige os operadores de bits

Qual usar: comece com membros comuns. Use campos de bits quando a memória importa (muitos registros) e os dados ficam dentro do programa. Use máscaras quando os bits saem do programa (arquivos, rede, hardware) ou quando você precisa testar várias flags de uma vez.


Quando dá errado

error: cannot take address of bit-field ‘hora’

O que significa: você usou & num campo de bits, quase sempre num scanf ou para criar um ponteiro. No Clang: address of bit-field requested.

Como consertar: leia numa variável comum (unsigned h;), confira o valor e copie: d.hora = h;. Se você precisa mesmo de ponteiro para o membro, ele não pode ser campo de bits.

warning: unsigned conversion from ‘int’ to ‘unsigned char:3’ changes value from ‘9’ to ‘1’ [-Woverflow]

O que significa: o número não cabe na largura do campo e vai ser cortado (só os bits da direita ficam). No Clang: implicit truncation from 'int' to bit-field changes value.

Como consertar: aumente a largura (com 3 bits, o máximo é 7; com 4, é 15). Lembre que, com variáveis, o corte acontece sem aviso: confira o valor antes de guardar.

O campo guarda 1, mas mostra -1

O que significa: o campo é int (com sinal) de largura 1, que só guarda 0 e −1. O gcc avisa comparison is always false due to limited range of data type se você comparar com 1; o Clang avisa na atribuição.

Como consertar: declare como unsigned ativo : 1; ou bool ativo : 1;.

error: width of ‘grande’ exceeds its type

O que significa: a largura pedida é maior que o número de bits do tipo (32 para unsigned).

Como consertar: diminua a largura ou use um tipo maior, como uint64_t.

error: ‘sizeof’ applied to a bit-field

O que significa: sizeof mede bytes, e um campo de bits não tem tamanho em bytes. No Clang: invalid application of 'sizeof' to bit-field.

Como consertar: meça a struct inteira (sizeof d). A largura do campo é a que você escreveu depois dos dois-pontos.


Referência rápida

EscritaO que faz
unsigned nome : N;Campo sem sinal de N bits: guarda de 0 a 2N − 1
signed int nome : N;Campo com sinal: de −2N−1 a 2N−1 − 1 (com N = 1: só 0 e −1)
bool nome : 1;Campo sim/não (0 ou 1)
unsigned : N;N bits sem nome, reservados (espaço vazio)
unsigned : 0;O próximo campo começa numa nova unidade de armazenamento
x.nome = v;Guarda v; se não couber, ficam só os N bits da direita
&x.nome, sizeof x.nomeErro de compilação: campo de bits não tem endereço nem tamanho em bytes
sizeof xTamanho da struct inteira (múltiplo do tamanho da unidade, 4 bytes para unsigned)

Exercícios

1. Largura do mês

Complete com a menor largura que guarda os meses de 1 a 12:

unsigned mes : ;

2. Sim ou não

Complete com o tipo que guarda 0 ou 1 num campo de 1 bit sem a pegadinha do −1 (escreva um tipo de uma palavra só, sem include):

ligado : 1;

3. Um campo unsigned nivel : 3; recebe v.nivel = 10;. O que fica guardado?

4. Por que scanf("%u", &d.hora); não compila quando hora é campo de bits?

5. Uma struct tem só unsigned a : 10; unsigned b : 10;. Quanto dá o sizeof no gcc do laboratório?

6. A Ana quer gravar num arquivo um cabeçalho de 32 bits com posições de bits definidas por um documento oficial. O que usar?

Missão final: a data em 4 bytes

Missão: a agenda da Ana guarda milhares de datas, e cada uma ocupa 12 bytes. Transforme os três membros em campos de bits com a menor largura possível para cada um (dia de 1 a 31, mês de 1 a 12, ano até 4095), para a data caber em 4 bytes. A saída tem que ser:

17/10/2026
sizeof: 4
#include <stdio.h>

struct Data {
    unsigned dia;
    unsigned mes;
    unsigned ano;
};

int main(void) {
    struct Data hoje = {17, 10, 2026};
    printf("%02u/%02u/%u\n", hoje.dia, hoje.mes, hoje.ano);
    printf("sizeof: %zu\n", sizeof hoje);
    return 0;
}
#include <stdio.h>

struct Data {
    unsigned dia : 5;
    unsigned mes : 4;
    unsigned ano : 12;
};

int main(void) {
    struct Data hoje = {17, 10, 2026};
    printf("%02u/%02u/%u\n", hoje.dia, hoje.mes, hoje.ano);
    printf("sizeof: %zu\n", sizeof hoje);
    return 0;
}

No laboratório, a solução mostrou as mesmas duas linhas: 5 + 4 + 12 = 21 bits, uma unidade de 32 bits só. Experimente pôr ano : 11: o 2026 ainda cabe (11 bits vão até 2047), mas a agenda quebraria em 2048.


Resumo

  • Campo de bits: unsigned nome : N; dentro de uma struct. O membro ocupa só N bits, e vários campos dividem a mesma unidade (um unsigned de 32 bits).
  • Com N bits sem sinal cabem de 0 a 2N − 1. Escolha a menor largura em que o maior valor cabe.
  • O despertador da Ana caiu de 16 para 4 bytes. A economia importa quando há muitos registros.
  • Valor que não cabe é cortado (ficam os bits da direita). Com número fixo, o compilador avisa (-Woverflow); com variável, não: confira antes.
  • Use unsigned ou bool. Um int x : 1 só guarda 0 e −1.
  • Campo de bits não tem endereço (&, scanf, ponteiro) nem sizeof, e não existe array de campos.
  • A posição dos campos dentro da unidade depende do compilador. Para arquivos, rede e hardware, use máscaras.