Aula 05 – Operadores Lógicos: &&, || e !

Combinando condições, avaliação em curto-circuito e comparação com Java

O que são Operadores Lógicos?

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.

Em C, o resultado de uma expressão lógica é sempre um inteiro: 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

Operador AND: &&

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.

Tabela verdade do AND

ABA && 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.

DICA — usando inteiros como booleanos: Em C, qualquer variável 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).

Operador OR: ||

O operador || retorna verdadeiro quando pelo menos uma das condições é verdadeira. Só retorna falso quando ambas são falsas.

Tabela verdade do OR

ABA || 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

Operador NOT: !

O operador ! é unário — age sobre um único operando — e inverte seu valor lógico: transforma verdadeiro em falso e vice-versa.

Tabela verdade do NOT

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

Truque do !! (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.

Avaliação em Curto-Circuito

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:

#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.

Por que isso importa na prática? O curto-circuito é muito útil para evitar operações perigosas. Por exemplo, ao trabalhar com ponteiros (que veremos mais à frente), podemos verificar se um ponteiro é válido antes de tentar acessar o que ele aponta:
if (ptr != NULL && ptr->valor > 0) { ... }
// Se ptr for NULL, a segunda condição nem é avaliada — sem crash.

Exemplo do Material de Referência

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).

Combinando Operadores Lógicos

É 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

Cuidado com a precedência entre && e ||: 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.

Operadores Lógicos em C versus Java

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ódigo equivalente nas duas linguagens

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");
}

Exercícios — Aula 05

Teste seus conhecimentos sobre operadores lógicos em C:

1. Dado int a = 1, b = 0;, qual é o valor de a && b?

2. Qual será a saída do código abaixo?

int x = 5;
printf("%d\n", !x);
printf("%d\n", !!x);

3. O que é avaliação em curto-circuito no operador &&?

4. Analise o código. Qual a saída considerando a = 0 e b = 5?

if (a != 0 && b > 3)
    printf("A\n");
else
    printf("B\n");

5. Qual das expressões abaixo representa corretamente "x está entre 1 e 10 (inclusive)"?

6. Em C, qual é a diferença no tratamento de operandos lógicos em relação ao Java?

7. Qual é a precedência correta entre os operadores lógicos (do maior para o menor)?

8. Dado int vip = 0, convidado = 1, pagante = 0;, qual o resultado de vip || (convidado && !pagante)?