Operadores: aritméticos, lógicos, bit a bit
1. Introdução aos Operadores em C
Operadores são símbolos que instruem o compilador a realizar operações matemáticas, lógicas ou de manipulação de bits sobre operandos. Em C, eles são classificados em três categorias principais quanto ao número de operandos: unários (um operando), binários (dois operandos) e ternários (três operandos). A precedência determina a ordem de avaliação em expressões com múltiplos operadores, enquanto a associatividade define a direção (esquerda para direita ou direita para esquerda) quando operadores de mesma precedência aparecem. Dominar esses conceitos é fundamental para escrever expressões corretas e previsíveis.
2. Operadores Aritméticos
Os operadores aritméticos básicos em C são + (adição), - (subtração), * (multiplicação), / (divisão) e % (módulo). A divisão merece atenção especial: quando ambos os operandos são inteiros, o resultado é truncado (parte fracionária descartada). Para obter resultado de ponto flutuante, ao menos um operando deve ser float ou double.
#include <stdio.h>
int main() {
int a = 10, b = 3;
float x = 10.0, y = 3.0;
printf("Divisao inteira: %d / %d = %d\n", a, b, a / b); // 3
printf("Divisao real: %.1f / %.1f = %.2f\n", x, y, x / y); // 3.33
printf("Modulo: %d %% %d = %d\n", a, b, a % b); // 1
return 0;
}
Os operadores de incremento (++) e decremento (--) são unários e existem nas formas pré-fixada e pós-fixada. Na forma pré-fixada (++x), o valor é incrementado antes de ser usado na expressão; na pós-fixada (x++), o valor original é usado primeiro.
#include <stdio.h>
int main() {
int x = 5;
printf("Pre-fixado: %d\n", ++x); // 6 (incrementa, depois imprime)
printf("Pos-fixado: %d\n", x++); // 6 (imprime, depois incrementa)
printf("Apos tudo: %d\n", x); // 7
return 0;
}
3. Operadores Relacionais e Lógicos
Os operadores relacionais comparam valores e retornam 1 (verdadeiro) ou 0 (falso): == (igual), != (diferente), <, >, <=, >=. Já os operadores lógicos combinam expressões booleanas: && (E lógico), || (OU lógico) e ! (NÃO lógico).
Uma característica crucial é o curto-circuito: em expr1 && expr2, se expr1 for falsa, expr2 não é avaliada. Em expr1 || expr2, se expr1 for verdadeira, expr2 é ignorada. Isso pode evitar erros ou causar efeitos colaterais inesperados.
#include <stdio.h>
int main() {
int idade = 17;
int tem_autorizacao = 1;
// Curto-circuito: se idade >= 18 for falso, tem_autorizacao nem é verificado
if (idade >= 18 && tem_autorizacao) {
printf("Pode entrar\n");
} else {
printf("Entrada negada\n");
}
// Exemplo de efeito colateral com curto-circuito
int a = 0, b = 5;
if (a != 0 && b / a > 2) { // a != 0 é falso, então b/a nunca é executado
printf("Nao chega aqui\n");
}
return 0;
}
4. Operadores de Atribuição e Atribuição Composta
O operador de atribuição simples = armazena o valor da direita na variável da esquerda. As atribuições compostas combinam operação aritmética ou lógica com atribuição: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=. A expressão x += 5 equivale a x = x + 5, mas é mais concisa e potencialmente mais eficiente.
#include <stdio.h>
int main() {
int valor = 10;
valor += 5; // valor = 15
valor *= 2; // valor = 30
valor %= 7; // valor = 2 (resto de 30/7)
printf("Valor final: %d\n", valor);
// Armadilha comum: confundir = com ==
int teste = 0;
if (teste = 5) { // atribui 5 a teste, expressão vale 5 (verdadeiro)
printf("Sempre entra! (teste = %d)\n", teste);
}
return 0;
}
5. Operadores Bit a Bit (Bitwise)
Os operadores bit a bit atuam diretamente nos bits dos operandos inteiros: & (E bit a bit), | (OU bit a bit), ^ (XOR bit a bit), ~ (complemento bit a bit), << (deslocamento à esquerda) e >> (deslocamento à direita). São fundamentais para programação de sistemas, controle de hardware e otimizações.
#include <stdio.h>
int main() {
unsigned char a = 0b11001100; // 204 em decimal
unsigned char b = 0b10101010; // 170 em decimal
printf("a & b = 0x%02X\n", a & b); // 10001000 (0x88)
printf("a | b = 0x%02X\n", a | b); // 11101110 (0xEE)
printf("a ^ b = 0x%02X\n", a ^ b); // 01100110 (0x66)
printf("~a = 0x%02X\n", (unsigned char)~a); // 00110011 (0x33)
// Deslocamentos
unsigned char c = 0b00000001; // 1
printf("c << 3 = %d\n", c << 3); // 8 (1 * 2^3)
printf("c >> 1 = %d\n", c >> 1); // 0 (divisao inteira por 2)
// Uso prático: máscaras para ativar/desativar flags
unsigned char flags = 0b00000000;
flags |= 0b00000100; // Ativa o bit 2
flags &= ~0b00000010; // Desativa o bit 1
printf("Flags: 0x%02X\n", flags); // 0x04
return 0;
}
6. Outros Operadores Relevantes
O operador ternário ? : é uma forma compacta de if-else: condicao ? valor_verdadeiro : valor_falso. O operador sizeof retorna o tamanho em bytes de um tipo ou variável (do tipo size_t). O cast permite conversão explícita de tipos: (tipo)expressao.
#include <stdio.h>
int main() {
int nota = 7;
char* resultado = (nota >= 6) ? "Aprovado" : "Reprovado";
printf("Resultado: %s\n", resultado);
printf("sizeof(int) = %zu bytes\n", sizeof(int));
printf("sizeof(double) = %zu bytes\n", sizeof(double));
// Cast explícito
int x = 10, y = 3;
float divisao = (float)x / y; // converte x para float antes da divisao
printf("Divisao com cast: %.2f\n", divisao);
return 0;
}
7. Precedência e Associatividade na Prática
A tabela abaixo resume a precedência dos operadores abordados (do maior para o menor):
| Operadores | Associatividade |
|---|---|
() [] -> . |
Esquerda → Direita |
++ -- + - ! ~ (tipo) * & sizeof |
Direita → Esquerda |
* / % |
Esquerda → Direita |
+ - |
Esquerda → Direita |
<< >> |
Esquerda → Direita |
< <= > >= |
Esquerda → Direita |
== != |
Esquerda → Direita |
& |
Esquerda → Direita |
^ |
Esquerda → Direita |
| |
Esquerda → Direita |
&& |
Esquerda → Direita |
|| |
Esquerda → Direita |
? : |
Direita → Esquerda |
= += -= etc. |
Direita → Esquerda |
A regra de ouro: use parênteses sempre que houver dúvida. Eles tornam a intenção explícita e evitam ambiguidades.
#include <stdio.h>
int main() {
int a = 5, b = 10, c = 15;
// Sem parenteses: ambiguo
int resultado = a + b * c; // 5 + (10*15) = 155
printf("Sem parenteses: %d\n", resultado);
// Com parenteses: intencao clara
resultado = (a + b) * c; // (5+10)*15 = 225
printf("Com parenteses: %d\n", resultado);
return 0;
}
8. Exemplos Integrados e Boas Práticas
Um exemplo que combina operadores aritméticos, lógicos e bit a bit para simular um sistema de permissões:
#include <stdio.h>
// Definicoes de permissoes como bits
#define PERM_LEITURA 0b0001
#define PERM_ESCRITA 0b0010
#define PERM_EXECUCAO 0b0100
#define PERM_ADMIN 0b1000
int main() {
unsigned char permissoes = 0;
// Conceder permissoes usando OR bit a bit
permissoes |= PERM_LEITURA;
permissoes |= PERM_EXECUCAO;
printf("Permissoes iniciais: 0x%02X\n", permissoes);
// Verificar se tem permissao de escrita (usando AND bit a bit)
if (permissoes & PERM_ESCRITA) {
printf("Tem permissao de escrita\n");
} else {
printf("Nao tem permissao de escrita\n");
}
// Conceder escrita se for admin (usando operador ternario)
int eh_admin = 1;
permissoes |= eh_admin ? PERM_ADMIN | PERM_ESCRITA : 0;
// Verificar multiplas permissoes com operadores logicos
if ((permissoes & PERM_LEITURA) && (permissoes & PERM_EXECUCAO)) {
printf("Pode ler e executar\n");
}
printf("Permissoes finais: 0x%02X\n", permissoes);
// Cuidado com efeitos colaterais em expressoes complexas
int contador = 0;
if (contador++ > 0 && (permissoes & PERM_ADMIN)) {
// contador é incrementado mesmo se a condicao falhar? Nao, devido ao curto-circuito
printf("Nao executa porque contador++ retorna 0\n");
}
printf("Contador apos expressao: %d\n", contador); // 1
return 0;
}
Boas práticas essenciais:
- Prefira legibilidade a código excessivamente compacto
- Use parênteses para explicitar precedência, mesmo quando desnecessários
- Evite efeitos colaterais dentro de expressões lógicas ou de printf
- Em operações bit a bit, use constantes nomeadas (macros ou enums) para melhor documentação
- Lembre-se que = é atribuição e == é comparação — um erro clássico e perigoso
Referências
- Operators in C (GeeksforGeeks) — Guia completo sobre todos os operadores em C, com exemplos práticos e tabela de precedência.
- C Operator Precedence (cppreference.com) — Tabela oficial de precedência e associatividade dos operadores em C.
- Bitwise Operators in C (Programiz) — Tutorial detalhado sobre operadores bit a bit, incluindo exemplos de máscaras e deslocamentos.
- Logical Operators in C (TutorialsPoint) — Explicação clara sobre operadores lógicos e curto-circuito em C.
- The GNU C Reference Manual - Operators — Documentação oficial GNU sobre operadores em C, incluindo aritméticos, lógicos e bit a bit.