C Operadores de Bits
Para o computador, todo número é uma fileira de bits, zeros e uns. Os operadores que você já conhece (+, *, <) trabalham com o número inteiro. Os operadores de bits trabalham com cada bit separado: comparam bit a bit, invertem, empurram a fileira para um lado. O C, que nasceu para escrever sistemas operacionais, tem seis deles: &, |, ^, ~, << e >>. Eles são a base das permissões de arquivos, das cores, da compressão, dos protocolos de rede e dos jogos antigos.
- Ler um número em binário e escrever um em C:
0b1101C23. - Escrever uma função que mostra os bits de um número, e o
%bdo C23 (que funciona no gcc, mas não no editor). - Os operadores
&(E),|(OU),^(OU exclusivo) e~(NÃO), bit a bit. - Os deslocamentos
<<e>>, e por que eles multiplicam e dividem por 2. - As armadilhas:
~numuint8_t,x & 1 == 0,&no lugar de&&. - Os deslocamentos proibidos (negativos ou maiores que a largura), que são comportamento indefinido.
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 gcc -std=c23 -Wall -Wextra arquivo.c -o programa, e conferidos no editor do guia. A próxima aula, Máscaras e Flags, usa tudo isto para ligar, desligar e testar bits um por um.
A ideia: uma fileira de lâmpadas
Imagine 8 lâmpadas numa fileira, cada uma acesa (1) ou apagada (0). A da direita vale 1, a seguinte vale 2, depois 4, 8, 16… cada uma vale o dobro da vizinha da direita. O número é a soma das lâmpadas acesas:
| Posição do bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Vale | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| 13 em binário | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
13 = 8 + 4 + 1, então as lâmpadas 3, 2 e 0 estão acesas: 00001101. Os bits são numerados da direita para a esquerda, começando do 0. O bit 0 é o "menos significativo" (vale menos); o bit 7, o "mais significativo". (A aula Memória, Bits e Bytes conta essa história com mais calma.)
Em C, você pode escrever o mesmo número de três jeitos:
| Escrita | Base | Observação |
|---|---|---|
13 | decimal (10) | O jeito de sempre. |
0x0D | hexadecimal (16) | Cada dígito hexa são exatamente 4 bits: 0 = 0000, D = 1101. Por isso programadores adoram hexa para bits. |
0b1101 | binário (2) | C23 Oficial no C23; o gcc e o Clang já aceitavam antes, como extensão. |
0b1010'0101 | binário com separador | C23 O apóstrofo só separa os dígitos para você ler melhor; o compilador ignora. |
1. Ver os bits: a função mostra_bits
O printf tradicional não tem uma letra para binário. A solução clássica é escrever uma função que mostra os bits um por um. Ela usa dois operadores desta aula (>> e &); por enquanto, confie nela. Na seção 6 você vai entender cada pedaço, e na missão final vai escrevê-la sozinho.
#include <stdio.h>
void mostra_bits(unsigned char x) {
for (int i = 7; i >= 0; i--) {
printf("%d", (x >> i) & 1);
}
printf("\n");
}
int main(void) {
mostra_bits(13);
mostra_bits(0b1101);
mostra_bits(0x0D);
mostra_bits(255);
mostra_bits(0b1010'0101);
return 0;
}
- unsigned char x
- Um byte sem sinal: 8 bits, de 0 a 255. (Um
uint8_tda aula Inteiros de Tamanho Fixo serviria igual.) - for (int i = 7; i >= 0; i--)
- Visita os bits do 7 até o 0, da esquerda para a direita, que é a ordem em que lemos.
- (x >> i) & 1
- "Pegue o bit número
idex": dá 0 ou 1. Explicado na seção 6.
$ gcc -std=c23 -Wall -Wextra binario.c -o binario $ ./binario 00001101 00001101 « 0b1101: o mesmo número 00001101 « 0x0D: o mesmo número 11111111 10100101
As três primeiras linhas são iguais: 13, 0b1101 e 0x0D são o mesmo número, escrito em bases diferentes. A base é só um jeito de escrever; dentro do computador é sempre binário.
Experimente: mostre os bits de 1, 2, 4, 8 e 128. Que padrão você vê? Depois tente 100 e confira: 100 = 64 + 32 + 4.
#include <stdio.h>
void mostra_bits(unsigned char x) {
for (int i = 7; i >= 0; i--) {
printf("%d", (x >> i) & 1);
}
printf("\n");
}
int main(void) {
mostra_bits(13);
mostra_bits(255);
return 0;
}
O %b do C23
O C23 finalmente criou uma letra para binário no printf: %b C23. Ela aceita as mesmas opções das outras letras: largura com zeros (%08b) e o prefixo 0b com # (%#b):
#include <stdio.h>
int main(void) {
printf("13 em binário: %b\n", 13);
printf("Com 8 dígitos: %08b\n", 13);
printf("Com prefixo: %#b\n", 13);
printf("Fim\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra pctb.c -o pctb $ ./pctb 13 em binário: 1101 Com 8 dígitos: 00001101 Com prefixo: 0b1101 Fim
Funciona no laboratório: quem faz o trabalho do printf é a biblioteca (a glibc), não o compilador, e a glibc já aprendeu o %b. A biblioteca do editor ainda não. Lá, o mesmo programa compila sem aviso, mas as três primeiras linhas somem: o printf desiste quando encontra uma letra que não conhece, e só o Fim aparece. Por isso este guia usa a função mostra_bits, que funciona em qualquer lugar.
2. &: E, bit a bit
O & (lê-se "E") compara os dois números coluna por coluna. Em cada coluna, o resultado é 1 só se os dois bits forem 1:
| bit de a | bit de b | a & b | a | b | a ^ b |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
A tabela já mostra os três operadores de duas entradas. Agora com números de verdade:
#include <stdio.h>
void mostra_bits(unsigned char x) {
for (int i = 7; i >= 0; i--) {
printf("%d", (x >> i) & 1);
}
}
int main(void) {
unsigned char a = 12;
unsigned char b = 10;
printf("a = "); mostra_bits(a); printf(" (%d)\n", a);
printf("b = "); mostra_bits(b); printf(" (%d)\n", b);
printf("a & b = "); mostra_bits(a & b); printf(" (%d)\n", a & b);
printf("a | b = "); mostra_bits(a | b); printf(" (%d)\n", a | b);
printf("a ^ b = "); mostra_bits(a ^ b); printf(" (%d)\n", a ^ b);
return 0;
}
$ gcc -std=c23 -Wall -Wextra e_ou.c -o e_ou $ ./e_ou a = 00001100 (12) b = 00001010 (10) a & b = 00001000 (8) a | b = 00001110 (14) a ^ b = 00000110 (6)
Olhe as colunas dos bits 3 a 0. Em a & b, só o bit 3 está aceso, porque é o único aceso nos dois (1100 e 1010 → 1000). Pense no & como um filtro: ele deixa passar só os bits que estão acesos no outro número. É assim que se testa um bit, como você vai ver na seção 6.
3. |: OU, bit a bit
O | (lê-se "OU"; é a barra vertical, que no teclado brasileiro fica perto do Shift esquerdo) dá 1 se pelo menos um dos bits for 1. Na saída acima, 1100 | 1010 = 1110 (14): juntou as lâmpadas acesas dos dois. Pense no | como somar acessórios: ele acende bits sem apagar nenhum. É o operador para ligar bits e combinar opções, assunto da próxima aula.
4. ^: OU exclusivo (XOR)
O ^ (acento circunflexo; em inglês XOR, de exclusive or) dá 1 quando os bits são diferentes. 1100 ^ 1010 = 0110 (6). Pense nele como um interruptor de escada: onde o segundo número tem 1, o bit do primeiro inverte; onde tem 0, fica como estava.
Duas propriedades curiosas: x ^ x é sempre 0, e aplicar o mesmo ^ chave duas vezes devolve o número original. Com isso dá para "embaralhar" um recado de brincadeira:
#include <stdio.h>
int main(void) {
char msg[] = "Oi, Bia!";
char chave = 0x2A;
for (int i = 0; msg[i] != '\0'; i++) msg[i] = msg[i] ^ chave;
printf("Embaralhada: ");
for (int i = 0; msg[i] != '\0'; i++) printf("%02X ", (unsigned char)msg[i]);
printf("\n");
for (int i = 0; msg[i] != '\0'; i++) msg[i] = msg[i] ^ chave;
printf("De volta: %s\n", msg);
return 0;
}
$ gcc -std=c23 -Wall -Wextra segredo.c -o segredo $ ./segredo Embaralhada: 65 43 06 0A 68 43 4B 0B De volta: Oi, Bia!
Cada letra virou outro byte (mostrado em hexa com %02X) e voltou depois do segundo ^. Isso não é criptografia de verdade: quem souber o truque descobre a chave em segundos. Mas a ideia de "XOR com uma chave" aparece dentro de algoritmos sérios.
5. ~: NÃO, bit a bit
O ~ (til) recebe um número só e inverte todos os bits: 0 vira 1, 1 vira 0. Mas cuidado com uma surpresa:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint8_t x = 0x0F;
uint8_t y = ~x;
printf("~x direto: %d\n", ~x);
printf("guardado em y: %d\n", y);
printf("~0 = %d, ~5 = %d\n", ~0, ~5);
unsigned int u = 0;
printf("~0u = %u\n", ~u);
return 0;
}
$ gcc -std=c23 -Wall -Wextra nao.c -o nao $ ./nao ~x direto: -16 « esperava 240? guardado em y: 240 ~0 = -1, ~5 = -6 ~0u = 4294967295
Por que ~x deu -16 e não 240 (11110000)? Antes de qualquer conta, o C promove os tipos pequenos (char, uint8_t, short) para int, de 32 bits. Então o ~ inverteu 32 bits, não 8: 00000000 00000000 00000000 00001111 virou 11111111 11111111 11111111 11110000, que num int com sinal é −16. Ao guardar em y, um uint8_t, só os 8 bits da direita ficam: 240.
Com números com sinal, o bit da esquerda decide se o número é negativo, e os resultados ficam confusos (~0 é −1, ~5 é −6). Para mexer com bits, use unsigned, uint8_t, uint32_t… e guarde o resultado numa variável do tipo certo antes de comparar ou mostrar.
6. << e >>: empurrar os bits
x << n (deslocar para a esquerda) empurra todos os bits de x n casas para a esquerda; entram zeros pela direita. x >> n (deslocar para a direita) empurra para a direita, e os bits que saem pela direita são perdidos.
#include <stdio.h>
int main(void) {
printf("3 << 1 = %d\n", 3 << 1);
printf("3 << 2 = %d\n", 3 << 2);
printf("3 << 4 = %d\n", 3 << 4);
printf("1 << 10 = %d\n", 1 << 10);
printf("200 >> 1 = %d\n", 200 >> 1);
printf("200 >> 3 = %d\n", 200 >> 3);
printf("7 >> 1 = %d\n", 7 >> 1);
printf("-8 >> 1 = %d\n", -8 >> 1);
return 0;
}
$ gcc -std=c23 -Wall -Wextra desloca.c -o desloca $ ./desloca 3 << 1 = 6 3 << 2 = 12 3 << 4 = 48 1 << 10 = 1024 200 >> 1 = 100 200 >> 3 = 25 7 >> 1 = 3 « 3,5 arredondado para baixo -8 >> 1 = -4
3é11;3 << 2é1100= 12. Cada casa para a esquerda dobra o valor (como acrescentar um zero no fim de um número decimal multiplica por 10). Entãox << n=xvezes 2n.1 << né um número com só o bit n aceso:1 << 10= 1024. Esse é o truque mais usado da próxima aula.- Cada casa para a direita divide por 2, jogando fora o resto:
7 >> 1= 3. - Deslocar um negativo para a direita (
-8 >> 1) depende do compilador. O gcc e o Clang copiam o bit de sinal e dão −4, mas o padrão não garante. Mais um motivo para usar sem sinal.
Agora a mostra_bits faz sentido
Veja (x >> i) & 1 com x = 13 (00001101) e i = 2:
x >> 2empurra tudo duas casas para a direita:00000011. O bit que estava na posição 2 agora está na posição 0.& 1é um filtro com só o bit 0 aceso (00000001): apaga todos os outros bits. Sobra1.- Com
iindo de 7 até 0, o laço mostra cada bit, da esquerda para a direita.
Par ou ímpar com & 1
O bit 0 é o único que vale um número ímpar (1); todos os outros valem números pares. Então o número é ímpar se, e só se, o bit 0 estiver aceso:
Missão: complete a condição do if usando & 1 (e não %) para o programa dizer se cada número de 1 a 6 é par ou ímpar. Atenção aos parênteses: escreva (n & 1) == 0.
#include <stdio.h>
int main(void) {
for (int n = 1; n <= 6; n++) {
if (0 /* troque por: o bit 0 de n está apagado */) printf("%d é par\n", n);
else printf("%d é ímpar\n", n);
}
return 0;
}
#include <stdio.h>
int main(void) {
for (int n = 1; n <= 6; n++) {
if ((n & 1) == 0) printf("%d é par\n", n);
else printf("%d é ímpar\n", n);
}
return 0;
}
No laboratório, a solução mostrou 1 é ímpar, 2 é par e assim por diante até 6 é par. O n % 2 == 0 da aula Operadores Aritméticos faz o mesmo e é mais fácil de ler; o compilador transforma os dois no mesmo código. Prefira o % quando a ideia é "par", e o & quando a ideia é "bit".
7. Deslocamentos proibidos: comportamento indefinido
Um int tem 32 bits. Deslocar 32 casas ou mais, ou um número negativo de casas, não tem resposta definida em C: é comportamento indefinido (aula Comportamento Indefinido). Veja o que acontece na prática:
#include <stdio.h>
int main(void) {
int n = 32;
printf("1 << 32 escrito direto: %d\n", 1 << 32);
printf("1 << n, com n = 32: %d\n", 1 << n);
printf("1 << -1 escrito direto: %d\n", 1 << -1);
return 0;
}
$ gcc -std=c23 -Wall -Wextra ub.c -o ub
ub.c: In function ‘main’:
ub.c:5:46: warning: left shift count >= width of type [-Wshift-count-overflow]
5 | printf("1 << 32 escrito direto: %d\n", 1 << 32);
| ^~
ub.c:7:46: warning: left shift count is negative [-Wshift-count-negative]
7 | printf("1 << -1 escrito direto: %d\n", 1 << -1);
| ^~
$ ./ub
1 << 32 escrito direto: 0
1 << n, com n = 32: 1
1 << -1 escrito direto: -2147483648
- left shift count >= width of type
- "Quantidade do deslocamento à esquerda maior ou igual à largura do tipo". Você pediu 32 casas num tipo de 32 bits.
- left shift count is negative
- "Quantidade do deslocamento é negativa".
- 0 e 1 para a mesma conta
1 << 32escrito direto deu 0 (o compilador fez a conta); com a variável, deu 1 (o processador fez a conta, e o processador do laboratório só olha os 5 bits de baixo do 32, que valem 0). A mesma operação deu dois resultados no mesmo programa.
E no editor? Os avisos são parecidos (shift count >= width of type, shift count is negative), mas os resultados mudam de novo: lá, 1 << 32 escrito direto mostrou 1792244464, um número sem sentido, e 1 << -1 mostrou 0. É isso que "indefinido" quer dizer: qualquer resultado vale, e nenhum é confiável.
O bit 31 de um int
Também é indefinido deslocar um 1 para dentro do bit de sinal: 1 << 31 não cabe num int (o maior é 231 − 1). Nem o gcc nem o Clang avisam, e os dois mostram -2147483648. O jeito certo é deslocar um número sem sinal: 1u << 31 vale 2147483648, sem nada de indefinido. Para 64 bits, use UINT64_C(1) << 40.
- Desloque números sem sinal:
1u << n,x >> ncomxdo tipounsigned/uint32_t. - A quantidade
ntem que ficar entre 0 e a largura do tipo menos 1 (0 a 31 para 32 bits). Se ela vem de fora do programa, confira antes. - Nunca ignore os avisos
-Wshift-count-overflowe-Wshift-count-negative.
8. Armadilhas de escrita
A precedência: x & 1 == 0
Os operadores &, | e ^ têm precedência menor que == (aula Precedência). Então x & 1 == 0 é lido como x & (1 == 0):
#include <stdio.h>
int main(void) {
int x = 6;
if (x & 1 == 0) {
printf("%d é par\n", x);
} else {
printf("%d é ímpar\n", x);
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra prec.c -o prec
prec.c: In function ‘main’:
prec.c:5:11: warning: suggest parentheses around comparison in operand of ‘&’ [-Wparentheses]
5 | if (x & 1 == 0) {
| ^
$ ./prec
6 é ímpar « errado!
Suggest parentheses around comparison in operand of ‘&’ = "sugiro parênteses em volta da comparação usada com o &". O 1 == 0 deu 0, e 6 & 0 deu 0 (falso), então o programa foi para o else. O Clang explica ainda melhor: & has lower precedence than ==; == will be evaluated first ("o & tem precedência menor que o ==; o == será feito primeiro"). Conserto: (x & 1) == 0. Regra: sempre ponha parênteses em volta de contas com bits.
& não é &&; | não é ||
#include <stdio.h>
int main(void) {
int a = 2, b = 1;
printf("a && b = %d\n", a && b);
printf("a & b = %d\n", a & b);
printf("a || b = %d\n", a || b);
printf("a | b = %d\n", a | b);
return 0;
}
$ gcc -std=c23 -Wall -Wextra logico.c -o logico $ ./logico a && b = 1 a & b = 0 « 10 & 01: nenhum bit em comum a || b = 1 a | b = 3
O && (aula Operadores Lógicos) pergunta "os dois são verdadeiros?": 2 e 1 são diferentes de zero, então sim (1). O & compara bit a bit: 10 & 01 não tem nenhuma coluna com dois 1, então dá 0 (falso!). Trocar um pelo outro num if compila sem aviso e dá resultados errados de vez em quando, o pior tipo de erro. Além disso, && e || param assim que sabem a resposta; & e | sempre calculam os dois lados.
Atribuição composta
Como +=, cada operador de bits de duas entradas tem sua forma "faça e guarde": &=, |=, ^=, <<=, >>=.
#include <stdio.h>
int main(void) {
unsigned x = 5;
x <<= 2; printf("x <<= 2 -> %u\n", x);
x |= 1; printf("x |= 1 -> %u\n", x);
x &= 6; printf("x &= 6 -> %u\n", x);
x ^= 7; printf("x ^= 7 -> %u\n", x);
x >>= 1; printf("x >>= 1 -> %u\n", x);
return 0;
}
$ gcc -std=c23 -Wall -Wextra composto.c -o composto $ ./composto x <<= 2 -> 20 x |= 1 -> 21 x &= 6 -> 4 x ^= 7 -> 3 x >>= 1 -> 1
Siga com os bits: 5 é 101; <<= 2 → 10100 (20); |= 1 → 10101 (21); &= 6 (00110) → 00100 (4); ^= 7 (111) → 011 (3); >>= 1 → 1.
9. Formas diferentes e qual usar
| Para… | Com bits | Sem bits | Qual usar |
|---|---|---|---|
| Multiplicar por 8 | x << 3 | x * 8 | x * 8: o compilador já troca por deslocamento sozinho. Escreva a intenção. |
| Dividir por 4 | x >> 2 | x / 4 | x / 4 (com negativos, os dois arredondam diferente) |
| Par ou ímpar | (x & 1) == 0 | x % 2 == 0 | Tanto faz; o % é mais claro |
Pegar o bit n | (x >> n) & 1 | — | Bits |
| Guardar várias opções sim/não num número | |, &, ~ | Vários bool ou campos de bits | Veja Máscaras e Flags |
| Mostrar em binário | Função mostra_bits | printf("%b") C23 | A função (o %b ainda não funciona em todo lugar) |
<stdbit.h>: funções prontas para bits
O C23 também criou o cabeçalho <stdbit.h>, com funções como stdc_count_ones(x) (quantos bits acesos) e stdc_bit_width(x) (quantos bits são necessários). No laboratório, stdc_count_ones(200) deu 3 e stdc_bit_width(200) deu 8. A biblioteca do editor ainda não tem esse cabeçalho (o Clang diz fatal error: 'stdbit.h' file not found).
Quando dá errado
warning: suggest parentheses around comparison in operand of ‘&’
O que significa: você escreveu algo como x & 1 == 0, e o == é feito antes do &. O resultado quase nunca é o que você queria. No Clang: & has lower precedence than ==; == will be evaluated first.
Como consertar: parênteses em volta da conta de bits: (x & 1) == 0.
warning: left shift count >= width of type / left shift count is negative
O que significa: deslocamento de 32 casas ou mais num tipo de 32 bits, ou de um número negativo de casas. É comportamento indefinido: o resultado muda de compilador para compilador, e até dentro do mesmo programa.
Como consertar: use um tipo maior (UINT64_C(1) << 40) ou confira a quantidade antes (if (n < 32)).
~x deu um número negativo
O que significa: o x era pequeno (uint8_t, char) e foi promovido para int antes do ~. Os 24 bits de cima, que eram 0, viraram 1.
Como consertar: guarde o resultado no tipo certo (uint8_t y = ~x;) ou corte com uma máscara (~x & 0xFF).
printf("%b") não mostra nada no editor
O que significa: o %b é do C23 e depende da biblioteca. A glibc do laboratório tem; a biblioteca do editor não, e o printf para no meio sem mostrar a linha.
Como consertar: use uma função como a mostra_bits desta aula.
if (a & b) que falha "às vezes"
O que significa: provavelmente você queria &&. Com &, dois números verdadeiros (como 2 e 1) podem dar 0.
Como consertar: para "e" de condições, &&; para "ou", ||. Os operadores de um símbolo só são para bits.
Referência rápida
| Operador | Nome | Bit a bit, dá 1 quando… | Exemplo (8 bits) |
|---|---|---|---|
a & b | E (AND) | os dois bits são 1 | 1100 & 1010 = 1000 |
a | b | OU (OR) | pelo menos um é 1 | 1100 | 1010 = 1110 |
a ^ b | OU exclusivo (XOR) | os bits são diferentes | 1100 ^ 1010 = 0110 |
~a | NÃO (NOT) | o bit era 0 (inverte todos) | ~00001111 = 11110000 |
a << n | deslocar à esquerda | empurra n casas; entram zeros (= vezes 2n) | 0011 << 2 = 1100 |
a >> n | deslocar à direita | empurra n casas; os bits da direita somem (= dividido por 2n) | 1100 >> 2 = 0011 |
&= |= ^= <<= >>= | atribuição composta | faz a conta e guarda em a | x |= 1; |
(x >> n) & 1 | receita | o bit n de x (0 ou 1) | (13 >> 2) & 1 = 1 |
1u << n | receita | número com só o bit n aceso | 1u << 3 = 1000 |
Exercícios
1. E bit a bit
Quanto vale 6 & 3? (Dica: 6 é 110 e 3 é 011.)
2. Deslocamento
Quanto vale 5 << 3?
3. O operador que inverte
Complete com o operador que dá 1 só onde os bits são diferentes:
4. Com int x = 6;, o que if (x & 1 == 0) faz?
5. Com uint8_t x = 0x0F;, o que printf("%d", ~x); mostra?
6. Qual destas linhas é comportamento indefinido num int de 32 bits?
Missão final: escreva a sua mostra_bits
Missão: a função mostra_bits está vazia. Escreva o laço que mostra os 8 bits de x, do bit 7 até o bit 0, usando >> e & 1, e termine com uma quebra de linha. (Não vale %b: aqui no editor ele não funciona.) A saída tem que ser:
10100101 00000001 11111111
#include <stdio.h>
void mostra_bits(unsigned char x) {
// mostre os 8 bits de x, do 7 ao 0, e depois "\n"
}
int main(void) {
mostra_bits(0xA5);
mostra_bits(1);
mostra_bits(255);
return 0;
}
#include <stdio.h>
void mostra_bits(unsigned char x) {
for (int i = 7; i >= 0; i--) {
printf("%d", (x >> i) & 1);
}
printf("\n");
}
int main(void) {
mostra_bits(0xA5);
mostra_bits(1);
mostra_bits(255);
return 0;
}
Repare: 0xA5 é A (1010) seguido de 5 (0101). Cada dígito hexa vira exatamente 4 bits, e é por isso que o hexa é a "língua" preferida de quem trabalha com bits.
Resumo
- Todo número é uma fileira de bits; o bit
nvale 2n, contando da direita a partir do 0. Em C23, escreva binário com0b1101. - Para ver os bits, use uma função com
(x >> i) & 1. O%bdo C23 funciona na glibc do laboratório, mas não no editor. &(os dois),|(pelo menos um),^(diferentes) comparam coluna por coluna;~inverte todos os bits.<< nmultiplica por 2n,>> ndivide;1u << nacende só o bit n.- Use tipos sem sinal: tipos pequenos são promovidos para
int(o~de umuint8_tdá negativo), e o bit de sinal confunde tudo. - Deslocar um número negativo de casas, ou casas demais, ou um 1 para dentro do bit de sinal, é comportamento indefinido.
- Ponha parênteses:
(x & 1) == 0. E não confunda&/|com&&/||.