Até agora, nossos programas seguiam sempre o mesmo caminho, da primeira à última linha. Mas um programa útil precisa escolher: cobrar frete se o pedido for abaixo de um valor, mostrar "aprovado" se a nota passar de sete, encerrar se o usuário digitar "sair". Hoje damos ao C o poder de decidir. E, como sempre, vamos descobrir uma ou duas armadilhas que a linguagem reserva para os desatentos.
A decisão mais simples: if
O if executa um bloco apenas se uma condição for verdadeira:
#include <stdio.h>
int main(void) {
int nota = 8;
if (nota >= 7) {
printf("Aprovado!\n");
}
return 0;
}
A condição fica entre parênteses. Se ela for verdadeira, o bloco entre chaves roda; se não, é ignorado. Você pode omitir as chaves quando o bloco tem uma única linha, mas não faça isso. Chaves sempre presentes evitam uma classe inteira de bugs difíceis — voltaremos a esse ponto.
Os dois caminhos: if e else
O else cobre o caso contrário:
#include <stdio.h>
int main(void) {
int idade = 16;
if (idade >= 18) {
printf("Maior de idade.\n");
} else {
printf("Menor de idade.\n");
}
return 0;
}
E, para mais de dois caminhos, encadeamos else if:
#include <stdio.h>
int main(void) {
int nota = 6;
if (nota >= 9) {
printf("Excelente\n");
} else if (nota >= 7) {
printf("Aprovado\n");
} else if (nota >= 5) {
printf("Recuperação\n");
} else {
printf("Reprovado\n");
}
return 0;
}
O C testa as condições de cima para baixo e para na primeira verdadeira. Por isso a ordem importa: se você colocasse nota >= 5 antes de nota >= 9, ninguém jamais seria "Excelente".
Verdadeiro e falso, à moda do C
Aqui está um traço fundamental da linguagem: o C não tinha, originalmente, um tipo booleano. Para ele, zero é falso e qualquer coisa diferente de zero é verdadeira. Isso significa que uma condição não precisa ser uma comparação:
#include <stdio.h>
int main(void) {
int estoque = 3;
if (estoque) { // verdadeiro porque 3 != 0
printf("Ainda há %d unidades.\n", estoque);
}
return 0;
}
Desde o C99, existe o cabeçalho <stdbool.h>, que traz bool, true e false por comodidade — mas por baixo dos panos continuam sendo 1 e 0. Use-o para deixar a intenção clara:
#include <stdio.h>
#include <stdbool.h>
int main(void) {
bool logado = true;
if (logado) {
printf("Bem-vindo de volta.\n");
}
return 0;
}
Operadores de comparação e lógicos
Para construir condições, você combina operadores relacionais e lógicos:
== igual a && E (and)
!= diferente de || OU (or)
< menor que ! NÃO (not)
> maior que
<= menor ou igual
>= maior ou igual
Exemplo combinando os dois:
#include <stdio.h>
int main(void) {
int idade = 25;
double renda = 3500.0;
if (idade >= 18 && renda >= 2000.0) {
printf("Elegível para o crédito.\n");
} else {
printf("Não elegível.\n");
}
return 0;
}
Os operadores && e || têm avaliação em curto-circuito: em a && b, se a já for falso, o b nem é avaliado (o resultado já está decidido). Isso não é um detalhe — é uma técnica útil para proteger operações, como veremos ao estudar ponteiros.
A armadilha clássica: = em vez de ==
Este é possivelmente o erro mais famoso do C. Comparação é == (dois sinais). Um único = é atribuição, e o C aceita atribuição dentro de um if sem reclamar:
int x = 5;
if (x = 0) { // ATRIBUI 0 a x — e 0 é falso!
printf("nunca entra aqui\n");
}
// x agora vale 0, e você nem percebeu
O código compila, roda, e faz a coisa errada em silêncio. Duas defesas: compile sempre com gcc -Wall (ele avisa), e adote o hábito de escrever a constante à esquerda — if (0 == x) — pois aí um = acidental vira erro de compilação.
Veja o aviso aparecer: no Compiler Explorer, o campo ao lado do compilador aceita as mesmas flags da linha de comando. Cole o if com =, compile sem nada e depois com -Wall: só o segundo reclama do valor usado como condição. Acrescente -Wextra e deixe os dois ligados desde já.
O operador ternário: um if que devolve valor
Para escolhas simples entre dois valores, há um atalho — o operador ? ::
#include <stdio.h>
int main(void) {
int idade = 20;
const char *status = (idade >= 18) ? "adulto" : "menor";
printf("Status: %s\n", status);
return 0;
}
Leia como: "se idade >= 18, o valor é "adulto"; senão, "menor"". É elegante para casos curtos, mas não abuse — encadear ternários vira código ilegível rapidamente.
Escolha entre muitos casos: switch
Quando você compara uma mesma variável contra vários valores fixos, o switch é mais claro que uma escada de else if:
#include <stdio.h>
int main(void) {
int dia = 3;
switch (dia) {
case 1:
printf("Domingo\n");
break;
case 2:
printf("Segunda\n");
break;
case 3:
printf("Terça\n");
break;
default:
printf("Outro dia\n");
break;
}
return 0;
}
Três pontos merecem atenção. O break ao fim de cada case é obrigatório na prática: sem ele, a execução "escorrega" para o próximo caso (o chamado fall-through). O default cobre qualquer valor não previsto — como o else final. E o switch só funciona com valores inteiros ou caracteres, nunca com double ou strings.
O fall-through, aliás, às vezes é intencional — para agrupar casos:
#include <stdio.h>
int main(void) {
char c = 'e';
switch (c) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
printf("Vogal\n");
break;
default:
printf("Consoante\n");
}
return 0;
}
Aqui a ausência de break nos primeiros casos é proposital: qualquer vogal cai no mesmo bloco.
Um programa que junta tudo
#include <stdio.h>
int main(void) {
double valor = 150.0;
int itens = 4;
double frete;
if (valor >= 200.0) {
frete = 0.0;
} else if (itens >= 3) {
frete = 15.0;
} else {
frete = 25.0;
}
const char *faixa = (valor >= 200.0) ? "frete grátis" : "frete cobrado";
printf("Pedido: R$ %.2f, %d itens\n", valor, itens);
printf("Frete: R$ %.2f (%s)\n", frete, faixa);
printf("Total: R$ %.2f\n", valor + frete);
return 0;
}
Saída:
Pedido: R$ 150.00, 4 itens
Frete: R$ 15.00 (frete cobrado)
Total: R$ 165.00
O que vem a seguir
Agora o programa sabe decidir. No próximo artigo, ele vai aprender a repetir — e é aí que o computador realmente mostra sua força, fazendo milhões de vezes aquilo que faríamos uma só. Vamos aos laços while, do-while e for, e às palavras break e continue, que controlam o fluxo de dentro de um laço.
Fontes e leituras recomendadas
- The C Programming Language (K&R), Kernighan & Ritchie — Cap. 3, Control Flow
- cppreference —
ifstatement — https://en.cppreference.com/w/c/language/if - cppreference —
switchstatement — https://en.cppreference.com/w/c/language/switch - cppreference —
<stdbool.h>— https://en.cppreference.com/w/c/types/boolean - GCC — a opção
-Walle o aviso contra=em condições — https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
Exercícios
Exercício 1
Escreva um programa que leia (ou fixe) um número inteiro e imprima "par" ou "ímpar". Dica: use o operador resto %.
Ver resposta
✓ Resposta: O resto da divisão por 2 é 0 para pares:
#include <stdio.h>
int main(void) {
int n = 7;
if (n % 2 == 0) printf("par\n");
else printf("ímpar\n");
return 0;
}
Exercício 2
Usando else if, classifique uma temperatura em: "congelando" (< 0), "frio" (0 a 15), "agradável" (16 a 25) ou "quente" (> 25).
Ver resposta
✓ Resposta: A ordem das faixas precisa ser coerente (do menor para o maior, aproveitando o corte anterior):
#include <stdio.h>
int main(void) {
double t = 22.0;
if (t < 0) printf("congelando\n");
else if (t <= 15) printf("frio\n");
else if (t <= 25) printf("agradável\n");
else printf("quente\n");
return 0;
}
Exercício 3
Reescreva a classificação de vogal/consoante do artigo usando if/else if em vez de switch. Qual das duas versões você acha mais legível, e por quê?
Ver resposta
✓ Resposta: Uma versão com if:
if (c=='a' || c=='e' || c=='i' || c=='o' || c=='u')
printf("Vogal\n");
else
printf("Consoante\n");
O switch com fall-through costuma ser mais legível quando há muitos valores discretos e explícitos (fica claro que são cinco casos agrupados). O if com || é mais compacto, mas a linha cresce e fica mais fácil errar um operador. Ambos são corretos; a escolha é de estilo.
Exercício 4
Explique o que este trecho imprime e por quê: int x = 5; if (x = 0) printf("A\n"); else printf("B\n");
Ver resposta
✓ Resposta: Imprime B. O if (x = 0) atribui 0 a x (não compara). O resultado da atribuição é 0, que é falso, então o else roda e imprime "B". De quebra, x agora vale 0. É exatamente a armadilha do = no lugar de ==.
Exercício 5
Escreva um switch que receba um caractere de operação ('+', '-', '*', '/') e dois números double, e imprima o resultado da conta correspondente. Trate a divisão por zero.
Ver resposta
✓ Resposta:
#include <stdio.h>
int main(void) {
char op = '/';
double a = 10.0, b = 0.0;
switch (op) {
case '+': printf("%.2f\n", a + b); break;
case '-': printf("%.2f\n", a - b); break;
case '*': printf("%.2f\n", a * b); break;
case '/':
if (b != 0.0) printf("%.2f\n", a / b);
else printf("Erro: divisão por zero\n");
break;
default: printf("Operação inválida\n");
}
return 0;
}
Aqui o if (b != 0.0) protege a conta antes de dividir — a mesma disciplina defensiva que o C sempre pede de você.