Operações Bit a Bit
Operações bit a bit são como controlar uma fileira de interruptores de luz — cada luz corresponde a um bit, e você pode ligar, desligar ou inverter qualquer luz individual sem afetar as outras.
Revisão de Binário
Os computadores representam todos os dados internamente usando binário. Um byte = 8 bits, cada bit é 0 ou 1.
Decimal 42 = Binário 00101010
Decimal 255 = Binário 11111111
Inteiros com sinal usam representação em complemento de dois: o bit mais alto é o bit de sinal, 0 para positivo, 1 para negativo.
Decimal 5 = 00000101
Decimal -5 = 11111011 (complemento de dois)
Seis Operadores Bit a Bit
| Operador | Nome | Regra |
|---|---|---|
& |
AND bit a bit | Resultado é 1 apenas quando ambos os bits são 1 |
| |
OR bit a bit | Resultado é 1 quando pelo menos um bit é 1 |
^ |
XOR bit a bit | Resultado é 1 quando os dois bits são diferentes |
~ |
NOT bit a bit | 0 vira 1, 1 vira 0 |
<< |
Deslocamento à esquerda | Desloca bits à esquerda, preenche bits baixos com 0 |
>> |
Deslocamento à direita | Desloca bits à direita, preenche bits altos com bit de sinal (com sinal) ou 0 (sem sinal) |
AND Bit a Bit (&)
1010 (10)
& 1100 (12)
------
1000 (8)
Usos: limpar certos bits, verificar se certos bits são 1.
OR Bit a Bit (|)
1010 (10)
| 1100 (12)
------
1110 (14)
Usos: definir certos bits como 1.
XOR Bit a Bit (^)
1010 (10)
^ 1100 (12)
------
0110 (6)
Propriedades: a ^ a = 0, a ^ 0 = a. XOR é reversível — aplicar XOR duas vezes com o mesmo valor restaura o original.
NOT Bit a Bit (~)
~ 00101010 (42)
= 11010101 (-43, complemento de dois)
Para números sem sinal: ~0 = 255 (8 bits), ~0 = 0xFFFFFFFF (32 bits).
Deslocamento à Esquerda (<<)
5 << 2
= 00000101 << 2
= 00010100
= 20
Deslocar à esquerda por n é equivalente a multiplicar por 2^n. 5 << 2 = 5 * 4 = 20.
Deslocamento à Direita (>>)
20 >> 2
= 00010100 >> 2
= 00000101
= 5
Deslocar à direita por n é equivalente a dividir por 2^n (arredondado para zero).
Quatro Operações Bit Essenciais
Definir um Bit (como 1)
flags |= (1 << n);
Define o enésimo bit como 1, deixando todos os outros bits inalterados.
Limpar um Bit (como 0)
flags &= ~(1 << n);
~(1 << n) produz uma máscara que é toda 1s exceto por um 0 na posição n. A operação AND limpa apenas aquele bit.
Inverter um Bit
flags ^= (1 << n);
Operação XOR: 0^1=1, 1^1=0, o que implementa perfeitamente a inversão.
Verificar um Bit
if (flags & (1 << n)) {
}
Se o enésimo bit for 1, o resultado é não zero; se for 0, o resultado é zero.
Exemplo
#include <stdio.h>
void print_bits(unsigned char val) {
for (int i = 7; i >= 0; i--) {
printf("%d", (val >> i) & 1);
}
printf("\n");
}
int main(void) {
unsigned char flags = 0;
flags |= (1 << 3);
printf("Definir bit 3: ");
print_bits(flags);
flags |= (1 << 5);
printf("Definir bit 5: ");
print_bits(flags);
flags &= ~(1 << 3);
printf("Limpar bit 3: ");
print_bits(flags);
flags ^= (1 << 5);
printf("Inverter bit 5: ");
print_bits(flags);
flags ^= (1 << 7);
printf("Inverter bit 7: ");
print_bits(flags);
if (flags & (1 << 7)) {
printf("Bit 7 é 1\n");
}
return 0;
}
Definir bit 3: 00001000
Definir bit 5: 00101000
Limpar bit 3: 00100000
Inverter bit 5: 00000000
Inverter bit 7: 10000000
Bit 7 é 1
Técnicas de Máscara
Uma máscara é uma combinação predefinida de bits usada para extrair ou modificar campos de bits específicos dentro de dados.
Extrair Bits Baixos
unsigned int val = 0xABCD;
unsigned int low_byte = val & 0xFF;
0xFF é uma máscara que mantém apenas os 8 bits mais baixos.
Extrair Bits Altos
unsigned int high_byte = (val >> 8) & 0xFF;
Primeiro desloca à direita por 8 bits, depois aplica máscara para obter os 8 bits baixos.
Combinar Valores
unsigned int combined = (high << 8) | low;
Junta dois bytes em um valor de 16 bits.
Exemplo
Extração de valor de cor RGB. Um valor de cor de 24 bits tem 8 bits cada para vermelho, verde e azul:
#include <stdio.h>
int main(void) {
unsigned int color = 0xFF6633;
unsigned char r = (color >> 16) & 0xFF;
unsigned char g = (color >> 8) & 0xFF;
unsigned char b = color & 0xFF;
printf("Cor #FF6633:\n");
printf(" Vermelho: %d\n", r);
printf(" Verde: %d\n", g);
printf(" Azul: %d\n", b);
unsigned int new_color = 0x00;
new_color |= ((r / 2) << 16);
new_color |= ((g / 2) << 8);
new_color |= (b / 2);
printf("Escurecida: #%06X\n", new_color);
return 0;
}
Cor #FF6633:
Vermelho: 255
Verde: 102
Azul: 51
Escurecida: #7F3319
Flags de Permissão
As permissões de arquivo do Linux são uma aplicação clássica de operações bit a bit. 9 bits de permissão representam dono/grupo/outros para leitura/escrita/execução:
rwxr-xr-x = 111101101 = 0755
rw-r--r-- = 110100100 = 0644
#include <stdio.h>
#define READ (1 << 2)
#define WRITE (1 << 1)
#define EXECUTE (1 << 0)
void show_permission(unsigned char perm) {
printf("%c", (perm & READ) ? 'r' : '-');
printf("%c", (perm & WRITE) ? 'w' : '-');
printf("%c", (perm & EXECUTE) ? 'x' : '-');
}
int main(void) {
unsigned char owner = READ | WRITE | EXECUTE;
unsigned char group = READ | EXECUTE;
unsigned char other = READ | EXECUTE;
printf("Permissões: ");
show_permission(owner);
show_permission(group);
show_permission(other);
printf("\n");
owner &= ~WRITE;
printf("Após remover escrita: ");
show_permission(owner);
show_permission(group);
show_permission(other);
printf("\n");
return 0;
}
Permissões: rwxr-xr-x
Após remover escrita: r-xr-xr-x
Truques Práticos com Bits
Trocar Duas Variáveis (Sem Temporária)
a ^= b;
b ^= a;
a ^= b;
Princípio: a propriedade auto-inversa do XOR. Porém, isso prejudica a legibilidade e não é recomendado no desenvolvimento real.
Verificar Par ou Ímpar
if (n & 1) {
}
Se o bit mais baixo for 1, o número é ímpar. Isso é mais rápido que n % 2.
Multiplicar/Dividir por Potências de 2
n << 1
n << 2
n >> 1
Compiladores tipicamente otimizam n * 2 em um deslocamento, mas deslocamentos são seguros apenas para inteiros positivos.
Calcular Potências de 2
unsigned int pow2 = 1u << n;
1u << 0 = 1, 1u << 1 = 2, 1u << 8 = 256... Deslocamento é muito mais rápido que pow(2, n).
Alinhar a uma Potência de 2
unsigned int aligned = (value + mask) & ~mask;
Por exemplo, alinhar a um limite de 4 bytes: (n + 3) & ~3.
#include <stdio.h>
int main(void) {
int values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < 10; i++) {
int aligned = (values[i] + 3) & ~3;
printf("%d -> %d\n", values[i], aligned);
}
return 0;
}
0 -> 0
1 -> 4
2 -> 4
3 -> 4
4 -> 4
5 -> 8
6 -> 8
7 -> 8
8 -> 8
9 -> 12
❓ Perguntas Frequentes
P: Por que operações de deslocamento são mais rápidas que multiplicação e divisão? R: Deslocamentos são instruções de CPU de ciclo único, enquanto multiplicação e divisão requerem múltiplos ciclos de clock. Porém, compiladores modernos otimizam automaticamente multiplicações por potências de 2 em deslocamentos, então não há necessidade de escrevê-las manualmente.
P: O que acontece se você deslocar além do número de bits? *R: Comportamento indefinido! Deslocar um int de 32 bits por 32 ou mais bits não é permitido. Você deve garantir que a contagem de deslocamento esteja entre 0 e sizeof(tipo)8-1.
P: Operações bit a bit podem ser usadas em números de ponto flutuante? R: Não. Operadores bit a bit funcionam apenas em tipos inteiros. Para manipular os bits de um número de ponto flutuante, você precisa copiar seus bytes para um inteiro usando
memcpy.
P: O que há de errado com a troca XOR? R: Se a e b apontam para a mesma memória (por exemplo, a mesma variável), a troca XOR zerará o valor. Além disso, é difícil de ler, e o compilador otimiza uma troca normal para ser tão eficiente quanto.
📖 Resumo
- Seis operadores bit a bit:
&,|,^,~,<<,>> - Defina um bit com
|=, limpe com&= ~, inverta com^=, verifique com& - Máscaras são a técnica central das operações bit a bit, usadas para extrair e combinar campos de bits
- Flags de permissão são combinadas com OR, verificadas com AND e limpas com NOT
- Deslocamento à esquerda multiplica por 2, deslocamento à direita divide por 2, alinhe com
(n + máscara) & ~máscara
📝 Exercícios
- Escreva uma função que retorne o número de bits 1 na representação binária de um inteiro (usando operações bit a bit, não dividindo por 2 em um laço)
- Escreva um programa que use operações bit a bit para ajustar o brilho de uma cor RGB (multiplique cada canal por um coeficiente e recombine)
- Escreva uma função que extraia os bits m até n de um inteiro de 32 bits (m < n, contando a partir do bit 0)



