Combinando condições, avaliação em curto-circuito e comparação com Java
Os operadores lógicos são usados para combinar ou inverter condições, permitindo construir expressões mais complexas do que um simples operador relacional consegue expressar. Eles são a base de toda lógica de decisão em programas — aparecem constantemente em if, while e for.
0 representa falso e qualquer valor diferente de zero representa verdadeiro. Os três operadores lógicos primários são && (AND), || (OR) e ! (NOT).
| Operador | Nome | Resultado verdadeiro quando... | Exemplo |
|---|---|---|---|
&& |
AND lógico | Ambos os operandos são verdadeiros | idade >= 18 && temID |
|| |
OR lógico | Pelo menos um operando é verdadeiro | isStudent || idade < 18 |
! |
NOT lógico | O operando é falso (inverte o valor) | !isStudent |
O operador && retorna verdadeiro somente quando ambas as condições são verdadeiras. Se a primeira for falsa, o resultado já é falso — independentemente da segunda.
| A | B | A && B |
|---|---|---|
| verdadeiro (1) | verdadeiro (1) | verdadeiro (1) |
| verdadeiro (1) | falso (0) | falso (0) |
| falso (0) | verdadeiro (1) | falso (0) |
| falso (0) | falso (0) | falso (0) |
#include <stdio.h>
int main() {
int idade = 20;
int temID = 1; // 1 = verdadeiro (tem documento)
// Ambas as condições precisam ser verdadeiras
if (idade >= 18 && temID) {
printf("Acesso liberado: maior de idade E tem documento.\n");
} else {
printf("Acesso negado.\n");
}
// Combinando três condições com &&
int nota = 75, faltas = 3, pagamento = 1;
if (nota >= 60 && faltas <= 25 && pagamento) {
printf("Aluno aprovado em todos os critérios.\n");
}
return 0;
}
Acesso liberado: maior de idade E tem documento.
Aluno aprovado em todos os critérios.
int pode ser usada diretamente como condição: if (temID) é equivalente a if (temID != 0). Isso torna o código mais conciso, especialmente com variáveis que funcionam como flags (ligado/desligado).
O operador || retorna verdadeiro quando pelo menos uma das condições é verdadeira. Só retorna falso quando ambas são falsas.
| A | B | A || B |
|---|---|---|
| verdadeiro (1) | verdadeiro (1) | verdadeiro (1) |
| verdadeiro (1) | falso (0) | verdadeiro (1) |
| falso (0) | verdadeiro (1) | verdadeiro (1) |
| falso (0) | falso (0) | falso (0) |
#include <stdio.h>
int main() {
int isStudent = 0; // não é estudante
int idade = 20;
// Basta UMA das condições ser verdadeira
if (isStudent || idade < 18) {
printf("Desconto disponível: estudante OU menor de idade.\n");
} else {
printf("Sem desconto disponível.\n"); // este executa
}
// Verificando faixa de validade
int mes = 13;
if (mes < 1 || mes > 12) {
printf("Mês inválido: %d\n", mes);
}
return 0;
}
Sem desconto disponível.
Mês inválido: 13
O operador ! é unário — age sobre um único operando — e inverte seu valor lógico: transforma verdadeiro em falso e vice-versa.
| A | !A |
|---|---|
| verdadeiro (1) | falso (0) |
| falso (0) | verdadeiro (1) |
#include <stdio.h>
int main() {
int isStudent = 0; // falso
int conectado = 1; // verdadeiro
if (!isStudent) {
printf("Usuário NÃO é estudante.\n"); // executa (inverte o 0)
}
if (!conectado) {
printf("Sem conexão.\n");
} else {
printf("Conectado ao sistema.\n"); // executa (inverte o 1 → falso → vai ao else)
}
// ! pode ser encadeado, mas evite excessos — prejudica a leitura
int x = 5;
printf("!x: %d\n", !x); // 0 (x é não-zero → verdadeiro → ! → falso)
printf("!!x: %d\n", !!x); // 1 (dupla negação — converte qualquer não-zero para 1)
return 0;
}
Usuário NÃO é estudante.
Conectado ao sistema.
!x: 0
!!x: 1
!! (dupla negação): !!x converte qualquer inteiro não-zero em exatamente 1, e zero permanece 0. É uma forma idiomática em C de "normalizar" um valor para booleano puro — equivalente a x != 0.
C — assim como Java — usa avaliação em curto-circuito (short-circuit evaluation) nos operadores && e ||. Isso significa que o segundo operando só é avaliado se for necessário:
A && B: se A for falso, B não é avaliado — o resultado já é falso.A || B: se A for verdadeiro, B não é avaliado — o resultado já é verdadeiro.
#include <stdio.h>
int main() {
int a = 0, b = 10;
// Curto-circuito com &&
// Como a == 0 (falso), b > 5 NUNCA é avaliado
if (a != 0 && b > 5) {
printf("Ambos verdadeiros.\n");
} else {
printf("Curto-circuito: b não foi verificado.\n"); // executa
}
// Curto-circuito com ||
// Como a == 0 é falso, b > 5 precisa ser avaliado
if (a != 0 || b > 5) {
printf("Pelo menos um é verdadeiro.\n"); // executa
}
return 0;
}
Curto-circuito: b não foi verificado.
Pelo menos um é verdadeiro.
if (ptr != NULL && ptr->valor > 0) { ... }
// Se ptr for NULL, a segunda condição nem é avaliada — sem crash.
O programa abaixo, diretamente da seção 6 do livro de referência do curso, demonstra os três operadores aplicados a um cenário de controle de acesso:
#include <stdio.h>
int main() {
int age = 20;
int hasID = 1; // 1 = tem documento
int isStudent = 0; // 0 = não é estudante
// AND lógico: precisa ser maior de idade E ter documento
if (age >= 18 && hasID) {
printf("Access granted: age >= 18 AND has ID.\n");
}
// OR lógico: estudante OU menor de idade recebe desconto
if (isStudent || age < 18) {
printf("Access with discount: student OR underage.\n");
} else {
printf("No discount available.\n");
}
// NOT lógico: inverte o valor de isStudent
if (!isStudent) {
printf("User is not a student (NOT isStudent).\n");
}
return 0;
}
Access granted: age >= 18 AND has ID.
No discount available.
User is not a student (NOT isStudent).
É possível encadear múltiplos operadores lógicos em uma única expressão. A precedência dos operadores lógicos em C é: ! (maior) → && → || (menor). Use parênteses para deixar a intenção explícita:
#include <stdio.h>
int main() {
int temperatura = 35;
int umidade = 80;
int chuva = 0;
// Condição de alerta: calor extremo E alta umidade, SEM chuva
if (temperatura > 30 && umidade > 70 && !chuva) {
printf("Alerta: risco de desidratação!\n");
}
// Misturando && e || — parênteses tornam a leitura mais clara
int vip = 0, convidado = 1, pagante = 0;
if (vip || (convidado && !pagante)) {
printf("Entrada liberada.\n");
}
// Validação de intervalo: nota entre 0 e 100
float nota = 85.5;
if (nota >= 0.0 && nota <= 100.0) {
printf("Nota válida: %.1f\n", nota);
}
return 0;
}
Alerta: risco de desidratação!
Entrada liberada.
Nota válida: 85.5
a || b && c é avaliado como a || (b && c), pois && tem maior precedência que ||. Quando misturar os dois, use parênteses para evitar ambiguidade — tanto para o compilador quanto para o leitor.
Os operadores &&, || e ! existem em ambas as linguagens com o mesmo símbolo e a mesma semântica geral. As diferenças estão no sistema de tipos e na flexibilidade que cada linguagem oferece:
| Aspecto | C | Java |
|---|---|---|
| Operadores disponíveis | &&, ||, ! |
&&, ||, ! (idênticos) |
| Tipo do resultado | int: 0 (falso) ou 1 (verdadeiro) |
boolean: false ou true |
| Operando de tipos numéricos | Válido — qualquer inteiro funciona como booleano | Inválido — o compilador exige boolean explícito |
| Curto-circuito | Sim, em && e || |
Sim, em && e || (idêntico) |
| Versões sem curto-circuito | Não há — em C usa-se operadores bit a bit & e | |
& e | funcionam em boolean sem curto-circuito |
if (x) com inteiro |
Válido — 0 é falso, qualquer outro valor é verdadeiro |
Inválido — exige expressão booleana explícita |
| Risco de bug silencioso | Alto — um inteiro pode ser usado como booleano sem aviso | Baixo — o compilador rejeita tipos incompatíveis |
| C | Java |
|---|---|
int idade = 20, temID = 1;
if (idade >= 18 && temID) {
printf("Liberado\n");
}
|
int idade = 20;
boolean temID = true;
if (idade >= 18 && temID) {
System.out.println("Liberado");
}
|
Teste seus conhecimentos sobre operadores lógicos em C: