C Máscaras e Flags
Um único unsigned tem 32 bits, e cada bit pode ser um interruptor independente: luz da sala acesa, luz da cozinha apagada; pizza com queijo, sem cebola; arquivo que pode ser lido, mas não executado. Cada interruptor é uma flag (bandeira). Para mexer em um sem estragar os outros, usamos uma máscara: um número com só os bits que interessam acesos. Esta aula ensina as quatro receitas (ligar, desligar, inverter, testar) e mostra onde elas aparecem na vida real, como no chmod 755 do Linux.
- O que é uma máscara e como criar uma com
1u << n. - As quatro receitas: ligar (
x |= m), desligar (x &= ~m), inverter (x ^= m) e testar (x & m). - Dar nomes às flags com
#definee comenum, e combiná-las com|. - Testar vários bits de uma vez: "todos" e "pelo menos um".
- As permissões
rwxdo Linux, os números octais (0755) e um programa que mostrarwxr-xr-x. - Separar e juntar pedaços de um número (as cores RGB).
- Os erros clássicos:
== 1,+no lugar de|, esquecer o~, macro sem parênteses.
Antes, leia Operadores de Bits: esta aula usa &, |, ^, ~ e << o tempo todo, e a função mostra_bits que você escreveu lá. 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 ideia: o molde de pintura
Para pintar só uma letra numa parede, você usa um molde vazado (um estêncil): a tinta passa pelos buracos e o resto da parede fica protegido. Uma máscara de bits é esse molde. Os bits acesos (1) da máscara são os "buracos": só eles são mexidos. Os bits apagados (0) protegem o resto do número.
| Quero… | Operador | Receita | Lembrete |
|---|---|---|---|
| Ligar o bit | | (OU) | x |= m; | "acrescenta" os bits de m |
| Desligar o bit | & e ~ | x &= ~m; | "tudo, menos m" |
| Inverter o bit | ^ (XOR) | x ^= m; | interruptor de escada |
| Testar o bit | & (E) | if (x & m) | filtro: sobra só o bit de m |
1. A máscara de um bit: 1u << n
Você viu na aula anterior que 1u << n é um número com só o bit n aceso: 1u << 0 é 00000001, 1u << 3 é 00001000. Essa é a máscara de um bit. O u deixa o 1 sem sinal, para o deslocamento ser seguro até o bit 31.
A Ana tem 8 lâmpadas em casa, numeradas de 0 a 7, e guarda o estado de todas num único unsigned char, a variável luzes. Veja as quatro receitas em ação:
#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) {
unsigned char luzes = 0;
printf("Começo: "); mostra_bits(luzes);
luzes |= (1u << 0);
printf("Liga a 0: "); mostra_bits(luzes);
luzes |= (1u << 3);
printf("Liga a 3: "); mostra_bits(luzes);
luzes |= (1u << 3);
printf("Liga a 3 de novo:"); mostra_bits(luzes);
luzes &= ~(1u << 0);
printf("Desliga a 0: "); mostra_bits(luzes);
luzes ^= (1u << 5);
printf("Inverte a 5: "); mostra_bits(luzes);
luzes ^= (1u << 5);
printf("Inverte a 5: "); mostra_bits(luzes);
return 0;
}
$ gcc -std=c23 -Wall -Wextra luzes.c -o luzes $ ./luzes Começo: 00000000 Liga a 0: 00000001 Liga a 3: 00001001 Liga a 3 de novo:00001001 « ligar o que já está ligado não muda nada Desliga a 0: 00001000 Inverte a 5: 00101000 Inverte a 5: 00001000 « inverter duas vezes volta ao começo
2. Ligar: x |= m
O | dá 1 onde qualquer um dos dois tem 1. Onde a máscara tem 0, o bit de x passa igual; onde a máscara tem 1, o resultado é 1, estivesse o bit aceso ou não:
00000001 luzes | 00001000 1u << 3 -------- 00001001 só o bit 3 mudou
Por isso "ligar a 3 de novo" não mudou nada. Ligar é seguro: pode repetir sem medo.
3. Desligar: x &= ~m
Desligar precisa de dois passos, e é a receita que mais gente erra:
~(1u << 0)inverte a máscara:00000001vira11111110. Agora todos os bits estão acesos, menos o que você quer desligar.luzes & 11111110: o&deixa passar todos os bits deluzes, menos o bit 0, que é forçado a 0.
00001001 luzes & 11111110 ~(1u << 0) -------- 00001000 só o bit 0 apagou
Se você esquecer o ~ e escrever luzes &= (1u << 0), o resultado é o contrário: o bit 0 fica como estava e todos os outros apagam. A missão abaixo pede para você desligar uma luz do jeito certo.
Missão: a Ana saiu da cozinha (luz 1) e quer apagar só essa luz. Complete a linha marcada usando &= e ~, sem mexer nas outras luzes. A segunda linha tem que ser depois: 00100101.
#include <stdio.h>
#define COZINHA (1u << 1)
void mostra_bits(unsigned char x) {
for (int i = 7; i >= 0; i--) {
printf("%d", (x >> i) & 1);
}
printf("\n");
}
int main(void) {
unsigned char luzes = 0b00100111;
printf("antes: "); mostra_bits(luzes);
// apague só a luz da cozinha aqui
printf("depois: "); mostra_bits(luzes);
return 0;
}
#include <stdio.h>
#define COZINHA (1u << 1)
void mostra_bits(unsigned char x) {
for (int i = 7; i >= 0; i--) {
printf("%d", (x >> i) & 1);
}
printf("\n");
}
int main(void) {
unsigned char luzes = 0b00100111;
printf("antes: "); mostra_bits(luzes);
luzes &= ~COZINHA;
printf("depois: "); mostra_bits(luzes);
return 0;
}
4. Inverter: x ^= m
O ^ inverte os bits onde a máscara tem 1 e deixa o resto igual. É o botão "liga/desliga" de um controle remoto: o mesmo botão acende se estava apagado e apaga se estava aceso. Na saída do luzes.c, inverter o bit 5 duas vezes voltou ao estado inicial.
5. Testar: x & m
O & com a máscara apaga todos os outros bits. Se o bit testado estava aceso, sobra um número diferente de zero (verdadeiro no if); se estava apagado, sobra 0 (falso):
#include <stdio.h>
int main(void) {
unsigned luzes = 0b00001010;
for (int n = 0; n < 4; n++) {
if (luzes & (1u << n)) {
printf("luz %d: acesa\n", n);
} else {
printf("luz %d: apagada\n", n);
}
}
printf("luzes & (1u << 3) = %u\n", luzes & (1u << 3));
return 0;
}
$ gcc -std=c23 -Wall -Wextra testa.c -o testa $ ./testa luz 0: apagada luz 1: acesa luz 2: apagada luz 3: acesa luzes & (1u << 3) = 8 « não é 1: é o valor do bit 3
Repare na última linha: o teste do bit 3 dá 8, não 1. O resultado de x & m é o próprio bit, no lugar dele. Para o if tanto faz (qualquer número diferente de zero é verdadeiro), mas comparar com 1 dá errado:
#include <stdio.h>
int main(void) {
unsigned luzes = 0b00001010;
if ((luzes & (1u << 3)) == 1) {
printf("luz 3 acesa\n");
} else {
printf("luz 3 apagada?!\n");
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra igual1.c -o igual1
igual1.c: In function ‘main’:
igual1.c:5:29: warning: bitwise comparison always evaluates to false [-Wtautological-compare]
5 | if ((luzes & (1u << 3)) == 1) {
| ^~
$ ./igual1
luz 3 apagada?! « mas ela está acesa
Bitwise comparison always evaluates to false = "esta comparação de bits é sempre falsa": o gcc percebeu que algo & 8 só pode dar 0 ou 8, nunca 1. O Clang do editor não avisa neste caso: o programa simplesmente dá a resposta errada. Escreva if (x & m) ou if ((x & m) != 0). Se você precisa de exatamente 0 ou 1 (para mostrar ou somar), use (x >> n) & 1, como na mostra_bits.
6. Flags com nome
1u << 3 não diz nada a quem lê o código. Na vida real, cada bit ganha um nome. A pizzaria da Ana guarda os ingredientes de um pedido num unsigned:
#include <stdio.h>
#define QUEIJO (1u << 0)
#define CALABRESA (1u << 1)
#define CEBOLA (1u << 2)
#define AZEITONA (1u << 3)
void mostra_pedido(unsigned pedido) {
printf("Pizza com:");
if (pedido & QUEIJO) printf(" queijo");
if (pedido & CALABRESA) printf(" calabresa");
if (pedido & CEBOLA) printf(" cebola");
if (pedido & AZEITONA) printf(" azeitona");
printf(" (código %u)\n", pedido);
}
int main(void) {
unsigned pedido = QUEIJO | CEBOLA;
mostra_pedido(pedido);
pedido |= AZEITONA;
mostra_pedido(pedido);
pedido &= ~CEBOLA;
mostra_pedido(pedido);
return 0;
}
- #define QUEIJO (1u << 0)
- Dá um nome à máscara do bit 0 (aula Macros). Os parênteses em volta são importantes, como a seção "Quando dá errado" mostra.
- QUEIJO | CEBOLA
- Combina duas flags: bits 0 e 2 acesos (
0101= 5). Leia o|como "e também". - void mostra_pedido(unsigned pedido)
- Uma função que recebe várias opções num único parâmetro. É o grande uso das flags: em vez de quatro parâmetros
bool, um número. - pedido |= AZEITONA;
- Acrescenta azeitona, sem mexer no resto.
- pedido &= ~CEBOLA;
- Tira a cebola, sem mexer no resto.
$ gcc -std=c23 -Wall -Wextra pizza.c -o pizza $ ./pizza Pizza com: queijo cebola (código 5) Pizza com: queijo cebola azeitona (código 13) Pizza com: queijo azeitona (código 9)
A biblioteca do sistema usa flags assim o tempo todo. A função open do Linux, que está por trás do fopen, recebe o modo como flags combinadas: open("dados.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644) quer dizer "só escrita, e também crie se não existir, e também apague o conteúdo antigo". O último argumento, 0644, são as permissões da seção 9.
Experimente: crie uma flag MILHO no bit 4, peça uma pizza com calabresa, cebola e milho, e depois troque a cebola por azeitona. Qual é o código do pedido no final?
#include <stdio.h>
#define QUEIJO (1u << 0)
#define CALABRESA (1u << 1)
#define CEBOLA (1u << 2)
#define AZEITONA (1u << 3)
void mostra_pedido(unsigned pedido) {
printf("Pizza com:");
if (pedido & QUEIJO) printf(" queijo");
if (pedido & CALABRESA) printf(" calabresa");
if (pedido & CEBOLA) printf(" cebola");
if (pedido & AZEITONA) printf(" azeitona");
printf(" (código %u)\n", pedido);
}
int main(void) {
unsigned pedido = QUEIJO | CEBOLA;
mostra_pedido(pedido);
return 0;
}
Flags com enum
Outra forma comum é um enum (aula Enums), que agrupa as flags debaixo de um nome de tipo. Os valores precisam ser potências de 2, escritas à mão:
#include <stdio.h>
enum Opcao {
OPC_NEGRITO = 1 << 0,
OPC_ITALICO = 1 << 1,
OPC_SUBLINHADO = 1 << 2,
};
int main(void) {
unsigned estilo = OPC_NEGRITO | OPC_SUBLINHADO;
printf("estilo = %u\n", estilo);
if (estilo & OPC_ITALICO) printf("itálico\n");
if (estilo & OPC_SUBLINHADO) printf("sublinhado\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra enumflags.c -o enumflags $ ./enumflags estilo = 5 sublinhado
- Sem o
= 1 << n, oenumnumeraria 0, 1, 2, 3… e1 | 2daria 3, que seria confundido com a quarta opção. Flags precisam ser 1, 2, 4, 8, 16… - A combinação
OPC_NEGRITO | OPC_SUBLINHADO(5) não é nenhum dos valores doenum. Por isso a variável que guarda a combinação éunsigned, e nãoenum Opcao. - C23 Dá para escolher o tipo por trás do
enum:enum Opcao : uint8_t { … };. No laboratório e no editor,sizeofde uma variável desseenumdeu 1.
7. Vários bits de uma vez
Uma máscara pode ter vários bits acesos. Aí surgem duas perguntas diferentes: "estão todos acesos?" e "algum está aceso?":
#include <stdio.h>
#define LER (1u << 2)
#define ESCREVER (1u << 1)
#define EXECUTAR (1u << 0)
int main(void) {
unsigned dono = LER | ESCREVER;
unsigned precisa = LER | ESCREVER;
unsigned qualquer = ESCREVER | EXECUTAR;
if ((dono & precisa) == precisa) printf("tem LER e ESCREVER\n");
if ((dono & qualquer) != 0) printf("tem ESCREVER ou EXECUTAR\n");
if ((dono & (ESCREVER | EXECUTAR)) == (ESCREVER | EXECUTAR)) printf("tem os dois\n");
else printf("não tem ESCREVER e EXECUTAR juntos\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra varios.c -o varios $ ./varios tem LER e ESCREVER tem ESCREVER ou EXECUTAR não tem ESCREVER e EXECUTAR juntos
| Pergunta | Receita |
|---|---|
Todos os bits de m estão acesos? | (x & m) == m |
Pelo menos um bit de m está aceso? | (x & m) != 0 |
Nenhum bit de m está aceso? | (x & m) == 0 |
Os parênteses em volta de x & m são obrigatórios: sem eles, o == é feito antes do & (veja "Quando dá errado").
8. Números octais: o zero que muda tudo
Antes das permissões, um detalhe do C que pega muita gente. Um número que começa com 0 é lido em octal (base 8, dígitos de 0 a 7):
#include <stdio.h>
int main(void) {
int a = 755;
int b = 0755;
int c = 010;
printf("755 = %d, 0755 = %d, 010 = %d\n", a, b, c);
printf("0755 em octal: %o\n", b);
return 0;
}
$ gcc -std=c23 -Wall -Wextra octal.c -o octal $ ./octal 755 = 755, 0755 = 493, 010 = 8 0755 em octal: 755
0755 é 7×64 + 5×8 + 5 = 493, e 010 é 8, não 10. Nenhum compilador avisa, porque é C válido. Nunca escreva zeros à esquerda "para alinhar" números decimais. O %o do printf mostra um número em octal. E por que alguém usaria octal? Porque cada dígito octal são exatamente 3 bits, e as permissões do Linux vêm em grupos de 3.
9. Na vida real: as permissões rwx e o chmod 755
Todo arquivo no Linux tem 9 bits de permissão, em três grupos de três: o que o dono pode fazer, o que o grupo pode fazer e o que os outros podem fazer. Em cada grupo: r (read, ler) vale 4, w (write, escrever) vale 2, x (execute, executar) vale 1. Veja no laboratório da Ana:
$ echo 'echo Olá, Ana' > ola.sh $ ls -l ola.sh -rw-rw-r-- 1 ana ana 15 out 17 10:45 ola.sh $ ./ola.sh bash: ./ola.sh: Permissão negada « ninguém tem o x $ chmod 755 ola.sh $ ls -l ola.sh -rwxr-xr-x 1 ana ana 15 out 17 10:45 ola.sh $ ./ola.sh Olá, Ana $ stat -c "%a %A" ola.sh 755 -rwxr-xr-x
O chmod 755 ("change mode", mudar o modo) recebe as permissões em octal, um dígito por grupo:
| dono: 7 | grupo: 5 | outros: 5 | |||||||
|---|---|---|---|---|---|---|---|---|---|
| Bit | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| Letra | r | w | x | r | w | x | r | w | x |
| 755 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
| Mostra | r | w | x | r | - | x | r | - | x |
7 = 4 + 2 + 1 (rwx), 5 = 4 + 1 (r-x). É exatamente o que o ls -l mostra depois do primeiro caractere (que diz se é arquivo - ou pasta d). Agora, o mesmo em C: um programa que recebe o modo e mostra as letras, testando cada um dos 9 bits com uma máscara:
#include <stdio.h>
void mostra_permissoes(unsigned modo) {
const char letras[] = "rwx";
for (int i = 8; i >= 0; i--) {
if (modo & (1u << i)) {
printf("%c", letras[(8 - i) % 3]);
} else {
printf("-");
}
}
printf(" (%o)\n", modo);
}
int main(void) {
mostra_permissoes(0755);
mostra_permissoes(0644);
mostra_permissoes(0600);
return 0;
}
- mostra_permissoes(0755)
- O 0 na frente faz o C ler em octal, como o
chmod. Sem ele, 755 seria outro número. - for (int i = 8; i >= 0; i--)
- Visita os 9 bits, do 8 (o
rdo dono) até o 0 (oxdos outros). - modo & (1u << i)
- Testa o bit
i. - letras[(8 - i) % 3]
- Escolhe a letra: o bit 8 dá
(8 - 8) % 3 = 0(r), o bit 7 dá 1 (w), o bit 6 dá 2 (x), e o padrão se repete a cada grupo de três.
$ gcc -std=c23 -Wall -Wextra permissoes.c -o permissoes $ ./permissoes rwxr-xr-x (755) rw-r--r-- (644) rw------- (600)
A mesma saída do ls -l. O 644 é o padrão para documentos (dono lê e escreve; os outros só leem); o 600 é para arquivos secretos, como chaves (só o dono).
umask
Por que o ola.sh nasceu rw-rw-r-- (664)? Programas pedem 666 ao criar um arquivo, e o sistema desliga os bits da "máscara de criação", o umask. No laboratório, o comando umask respondeu 0002: 0666 & ~0002 = 0664. É a receita "desligar" da seção 3, feita pelo próprio Linux.
10. Pedaços de um número: as cores RGB
Máscaras também servem para guardar vários números pequenos dentro de um grande, com posições que você controla. Uma cor de tela é 0xRRGGBB: 8 bits de vermelho, 8 de verde, 8 de azul. Para separar, desloque e corte com & 0xFF (8 bits acesos); para juntar, desloque cada pedaço para o lugar e combine com |:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint32_t cor = 0xFF8000;
unsigned r = (cor >> 16) & 0xFF;
unsigned g = (cor >> 8) & 0xFF;
unsigned b = cor & 0xFF;
printf("cor 0x%06X: R=%u G=%u B=%u\n", (unsigned)cor, r, g, b);
uint32_t nova = ((uint32_t)30 << 16) | ((uint32_t)144 << 8) | 255;
printf("R=30 G=144 B=255 -> 0x%06X\n", (unsigned)nova);
return 0;
}
$ gcc -std=c23 -Wall -Wextra rgb.c -o rgb $ ./rgb cor 0xFF8000: R=255 G=128 B=0 R=30 G=144 B=255 -> 0x1E90FF
0xFF8000 é laranja, e 0x1E90FF é um azul (o "dodger blue" das páginas web). Esse é o jeito portátil de fazer o que os campos de bits fazem: aqui você decide a posição exata de cada pedaço, igual em qualquer compilador, e pode gravar o número num arquivo ou mandar pela rede.
11. Qual usar
| Forma | Exemplo | Quando usar |
|---|---|---|
#define com parênteses | #define QUEIJO (1u << 0) | O mais comum em C; é como a biblioteca do sistema faz (O_CREAT) |
enum com potências de 2 | OPC_NEGRITO = 1 << 0, | Quando você quer as flags agrupadas e com nome de tipo (o depurador mostra os nomes) |
constexpr C23 | constexpr unsigned QUEIJO = 1u << 0; | Constante com tipo, sem macro (aula Constantes) |
| Campos de bits | unsigned queijo : 1; | Quando cada flag é lida e escrita separada e os dados ficam dentro do programa |
Vários bool | bool queijo; | Poucas flags e memória sobrando: o mais simples de ler |
Para flags, prefira um tipo sem sinal de tamanho conhecido: unsigned (32 flags) ou uint8_t, uint32_t, uint64_t (aula Inteiros de Tamanho Fixo).
Quando dá errado
warning: bitwise comparison always evaluates to false [-Wtautological-compare]
O que significa: você comparou x & m com 1 (ou outro valor que ele nunca pode ter). x & (1u << 3) dá 0 ou 8, nunca 1. O Clang do editor não avisa: o programa só erra.
Como consertar: teste com if (x & m) ou (x & m) != 0; para todos os bits de uma máscara, (x & m) == m.
warning: suggest parentheses around comparison in operand of ‘&’ (e self-comparison always evaluates to true)
O que significa: você escreveu dono & precisa == precisa. O == vem primeiro: precisa == precisa é sempre 1, e o teste vira dono & 1. No laboratório, com dono = LER, o programa respondeu não pode por um motivo totalmente diferente do certo.
Como consertar: (dono & precisa) == precisa.
+ e apareceu outra opção
O que significa: + e | dão o mesmo resultado só se as flags forem diferentes. Com a mesma flag duas vezes, o + "vai um" para o bit vizinho. No laboratório: QUEIJO | QUEIJO = 1, mas QUEIJO + QUEIJO = 2, que é a CALABRESA.
Como consertar: combine flags sempre com |; tire com & ~, nunca com -.
O que significa: faltou o ~: x &= m mantém só o bit de m.
Como consertar: x &= ~m;, "tudo menos m".
O que significa: a macro não tem parênteses. Com #define ESCREVER 1u << 1, a expressão ~ESCREVER vira ~1u << 1, que o C lê como (~1u) << 1. No laboratório, deu 4294967292 em vez de 4294967293: o bit 0 também ficou apagado. Nenhum aviso.
Como consertar: sempre #define ESCREVER (1u << 1), com parênteses em volta de tudo.
chmod deu permissões estranhas ou um número saiu diferente
O que significa: confusão entre decimal e octal. Em C, 0755 é octal (493) e 755 é decimal; um zero à esquerda "para alinhar" muda o número.
Como consertar: use o 0 só quando quiser octal (permissões). Para mostrar em octal, %o.
Referência rápida
| Receita | O que faz |
|---|---|
1u << n | Máscara com só o bit n aceso |
x |= m; | Liga os bits de m |
x &= ~m; | Desliga os bits de m |
x ^= m; | Inverte os bits de m |
if (x & m) | Algum bit de m está aceso (para um bit só: "o bit está aceso") |
(x & m) == m | Todos os bits de m estão acesos |
(x & m) == 0 | Nenhum bit de m está aceso |
(x >> n) & 1 | O bit n, como 0 ou 1 |
(x >> k) & 0xFF | Os 8 bits que começam na posição k |
(a << 16) | (b << 8) | c | Junta pedaços num número |
#define F (1u << n) | Flag com nome (sempre com parênteses) |
A | B | Combina flags |
0755, %o | Número octal (cada dígito = 3 bits); mostrar em octal |
Exercícios
1. Ligar
Complete para ligar a flag AZEITONA no pedido:
2. Desligar
Complete para tirar a cebola do pedido:
3. Octal
Quais são as permissões, em octal, de um arquivo rw-r----- (dono lê e escreve, grupo só lê, outros nada)?
4. Com unsigned x = 0b1000;, quanto vale x & (1u << 3)?
5. Qual linha testa se as duas flags LER e ESCREVER estão ligadas em p?
6. Por que as flags de um enum precisam valer 1, 2, 4, 8…?
Missão final: mostrar as permissões
Missão: escreva o corpo da função mostra_permissoes. Para cada bit de 8 até 0, teste o bit com modo & (1u << i): se estiver aceso, mostre a letra (r, w ou x, repetindo nessa ordem); se não, mostre -. Termine com \n. A saída tem que ser:
rwxr-xr-x rw-r--r-- rw-------
#include <stdio.h>
void mostra_permissoes(unsigned modo) {
const char letras[] = "rwx";
// para i de 8 até 0: teste o bit i de modo
// aceso: mostre letras[(8 - i) % 3]; apagado: mostre '-'
}
int main(void) {
mostra_permissoes(0755);
mostra_permissoes(0644);
mostra_permissoes(0600);
return 0;
}
#include <stdio.h>
void mostra_permissoes(unsigned modo) {
const char letras[] = "rwx";
for (int i = 8; i >= 0; i--) {
if (modo & (1u << i)) {
printf("%c", letras[(8 - i) % 3]);
} else {
printf("-");
}
}
printf("\n");
}
int main(void) {
mostra_permissoes(0755);
mostra_permissoes(0644);
mostra_permissoes(0600);
return 0;
}
Quer ir além? Teste também 0777 (todo mundo pode tudo, o que quase nunca é uma boa ideia) e 0700.
Resumo
- Uma flag é um bit que guarda sim/não; uma máscara é um número com só os bits que interessam acesos, como
1u << n. - Ligar:
x |= m. Desligar:x &= ~m. Inverter:x ^= m. Testar:if (x & m), que dá o valor do bit (8, não 1). - Dê nomes às flags com
#define F (1u << n)(com parênteses!) ou com umenumde potências de 2, e combine com|, nunca com+. - Vários bits:
(x & m) == m(todos),(x & m) != 0(algum). Sempre com parênteses em volta do&. - Um número que começa com 0 é octal:
0755= 493. Cada dígito octal são 3 bits, como os gruposrwxdochmod. - Deslocamento e
& 0xFFseparam pedaços de um número (RGB);<<e|juntam. É a forma portátil de fazer o que os campos de bits fazem.