C if e else

Até agora, todos os seus programas faziam sempre a mesma coisa, linha por linha, de cima para baixo. Um programa de verdade precisa decidir: se a senha está certa, entra; senão, mostra um erro. Se o estoque está baixo, avisa. Nesta aula você vai aprender o if ("se") e o else ("senão"), as ferramentas de decisão mais usadas do C, e as três armadilhas que pegam todo iniciante: as chaves esquecidas, o = no lugar do == e o ; depois do if.

Você vai aprender
  • Como rodar um bloco de código só quando uma condição é verdadeira, com if.
  • Como escolher entre dois caminhos com if … else.
  • O que o C aceita como condição (qualquer número: 0 é falso, o resto é verdadeiro).
  • Por que usar chaves sempre, e o aviso -Wmisleading-indentation.
  • As armadilhas if (x = 0) e if (x > 0);, e os erros ‘else’ without a previous ‘if’ e undeclared.

Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.


A ideia: a bifurcação na estrada

Você está dirigindo e chega numa bifurcação. Uma placa diz: "Se for para a praia, vá pela esquerda; senão, siga pela direita". Você lê a placa, responde à pergunta e segue um dos caminhos, nunca os dois. Logo depois, as duas estradas se juntam de novo e a viagem continua.

Um if é essa placa. Ele faz uma pergunta (a condição), e a resposta decide qual trecho de código roda. As perguntas você já sabe fazer: são as comparações (Operadores de Comparação) e as combinações com &&, || e ! (Operadores Lógicos).


1. O if sozinho

#include <stdio.h>

int main(void) {
    int estoque = 2;
    printf("Estoque: %d\n", estoque);
    if (estoque < 5) {
        printf("Atenção: estoque baixo!\n");
        printf("Peça mais ao fornecedor.\n");
    }
    printf("Fim do relatório.\n");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra sozinho.c -o sozinho
$ ./sozinho
Estoque: 2
Atenção: estoque baixo!          « 2 < 5 é verdade: o bloco rodou
Peça mais ao fornecedor.
Fim do relatório.                « esta linha está fora do if: roda sempre
if
Palavra reservada do C: "se". Sempre em minúsculas.
(estoque < 5)
A condição, obrigatoriamente entre parênteses. Se ela der "sim" (qualquer valor diferente de 0), o bloco roda; se der "não" (0), o bloco é pulado.
{ … }
O bloco: tudo o que está entre as chaves depende do if. Aqui são dois printf, e os dois rodam ou nenhum roda.
sem ; depois do )
A linha do if não termina com ponto e vírgula. O if só termina no fim do bloco. (A seção 5 mostra o que acontece se você puser.)
o recuo
As linhas de dentro ficam 4 espaços mais para a direita. O compilador ignora isso; é para você enxergar o que está dentro do bloco.

Mude o estoque para 10 e o programa mostra só Estoque: 10 e Fim do relatório.: o bloco inteiro é pulado.

Experimente: mude temperatura para valores acima e abaixo de 30 e veja quando o aviso aparece. Depois acrescente um segundo if, que mostra "Leve um casaco." quando a temperatura for menor que 15.

#include <stdio.h>

int main(void) {
    int temperatura = 33;
    printf("Hoje: %d graus\n", temperatura);
    if (temperatura > 30) {
        printf("Beba bastante água!\n");
    }
    return 0;
}

2. O caminho alternativo: else

Com o else ("senão"), você diz o que fazer quando a condição dá "não". Exatamente um dos dois blocos roda. Este programa pergunta a idade (o scanf é explicado em Entrada com scanf):

#include <stdio.h>

int main(void) {
    int idade = 0;
    printf("Digite sua idade: ");
    if (scanf("%d", &idade) != 1) {
        printf("Isso não é um número.\n");
        return 1;
    }
    if (idade >= 18) {
        printf("Pode tirar a carteira de motorista.\n");
    } else {
        printf("Faltam %d anos para a carteira.\n", 18 - idade);
    }
    printf("Fim do programa.\n");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra idade.c -o idade
$ ./idade
Digite sua idade: 17
Faltam 1 anos para a carteira.          « 17 >= 18 é falso: rodou o else
Fim do programa.
$ ./idade
Digite sua idade: 30
Pode tirar a carteira de motorista.     « 30 >= 18: rodou o if
Fim do programa.
$ ./idade
Digite sua idade: abc
Isso não é um número.                   « o scanf não leu nada: o programa parou com return 1
} else {
O else vem logo depois da chave que fecha o bloco do if. Ele não tem condição própria: é "todo o resto".
18 - idade
Dentro do bloco você pode fazer contas e usar as variáveis normalmente.
if (scanf(…) != 1)
Um if para conferir a entrada: o scanf devolve quantos valores conseguiu ler. Se não leu 1, o programa avisa e termina com return 1 (um código de saída diferente de 0 indica erro). Conferir o retorno do scanf é um hábito de segurança: sem isso, o programa seguiria com a idade valendo 0.
Fim do programa.
Depois do if/else, os dois caminhos se juntam e o programa continua normalmente.

"Faltam 1 anos" ficou feio. Para escolher entre "ano" e "anos" numa linha só, existe o operador ternário; com o que você sabe agora, daria para usar outro if.

Missão: o programa lê um número (a entrada preparada é 7) e diz se ele é par. Para números ímpares, ele não mostra nada. Acrescente um else que mostre que o número é ímpar:

7 é ímpar
#include <stdio.h>

int main(void) {
    int n = 0;
    if (scanf("%d", &n) != 1) {
        return 1;
    }
    if (n % 2 == 0) {
        printf("%d é par\n", n);
    }
    return 0;
}
#include <stdio.h>

int main(void) {
    int n = 0;
    if (scanf("%d", &n) != 1) {
        return 1;
    }
    if (n % 2 == 0) {
        printf("%d é par\n", n);
    } else {
        printf("%d é ímpar\n", n);
    }
    return 0;
}

O n % 2 é o resto da divisão por 2 (aula Operadores Aritméticos): 0 para pares, 1 para ímpares positivos. Troque a entrada para 10 e para −3 e rode de novo.


3. O que pode ser uma condição

O if aceita qualquer valor numérico entre os parênteses, com a regra que você viu em Operadores Lógicos: 0 é falso, qualquer outro número é verdadeiro.

#include <stdio.h>

int main(void) {
    int mensagens = 3;
    bool chovendo = false;
    if (mensagens) {
        printf("Você tem %d mensagens novas.\n", mensagens);
    }
    if (!chovendo) {
        printf("Pode deixar o guarda-chuva em casa.\n");
    }
    if (-1) {
        printf("-1 também é verdadeiro.\n");
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra condicao.c -o condicao
$ ./condicao
Você tem 3 mensagens novas.            « 3 é diferente de 0: verdadeiro
Pode deixar o guarda-chuva em casa.    « !false = true
-1 também é verdadeiro.                « negativo também não é 0

if (mensagens) é o mesmo que if (mensagens != 0). As duas formas aparecem muito em código C. Para quem está começando, a comparação escrita é mais clara; com variáveis bool C23 (aula Booleanos), if (chovendo) e if (!chovendo) são o jeito natural.


4. Chaves: use sempre

O C permite tirar as chaves quando o bloco tem uma linha só: if (x > 0) printf("positivo\n");. Parece prático, mas é a origem de um dos erros mais traiçoeiros. A Ana quis zerar o saldo quando a compra fosse maior que ele:

#include <stdio.h>

int main(void) {
    int saldo = 100;
    int compra = 30;
    if (compra > saldo)
        printf("Saldo insuficiente.\n");
        saldo = 0;
    printf("Saldo: %d\n", saldo);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra semchaves.c -o semchaves
semchaves.c: In function ‘main’:
semchaves.c:6:5: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    6 |     if (compra > saldo)
      |     ^~
semchaves.c:8:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
    8 |         saldo = 0;
      |         ^~~~~
$ ./semchaves
Saldo: 0         « a compra cabia no saldo, mas o saldo foi zerado!

Sem chaves, o if controla só a primeira instrução depois dele (o printf). A linha saldo = 0; está fora do if e roda sempre. O recuo engana os olhos, mas o compilador não olha recuo. O aviso diz: "esta cláusula if não protege… esta instrução, mas ela está recuada de um jeito enganoso, como se fosse protegida pelo if". O -Wmisleading-indentation ("recuo enganoso") vem com o -Wall.

Regra do guia: chaves sempre

Use { } em todo if e todo else, mesmo com uma linha só. Quando você (ou outra pessoa) acrescentar uma segunda linha depois, ela já vai cair dentro do bloco. Muitos guias de estilo profissionais, como o SEI CERT C, recomendam isso. Um erro desse tipo (um goto fail; duplicado, fora das chaves) deixou uma falha de segurança famosa nos aparelhos da Apple em 2014.

Missão: conserte o programa da Ana com chaves, para o saldo só ser zerado quando a compra for maior que ele. Sem avisos. Depois troque a compra para 150 e confira que aí, sim, o saldo zera.

Saldo: 100
#include <stdio.h>

int main(void) {
    int saldo = 100;
    int compra = 30;
    if (compra > saldo)
        printf("Saldo insuficiente.\n");
        saldo = 0;
    printf("Saldo: %d\n", saldo);
    return 0;
}
#include <stdio.h>

int main(void) {
    int saldo = 100;
    int compra = 30;
    if (compra > saldo) {
        printf("Saldo insuficiente.\n");
        saldo = 0;
    }
    printf("Saldo: %d\n", saldo);
    return 0;
}

Onde pôr as chaves

O compilador aceita a chave de abertura na mesma linha ou na linha de baixo:

if (nota >= 6) {          // estilo K&R: chave na mesma linha
    printf("Aprovada\n");
} else {
    printf("Reprovada\n");
}

if (nota >= 6)            // estilo Allman: chave na linha de baixo
{
    printf("Aprovada\n");
}
else
{
    printf("Reprovada\n");
}

O primeiro estilo vem do livro de Kernighan e Ritchie (K&R), os criadores do C, e é o mais comum em C (o código do Linux usa ele). Este guia usa o K&R. O importante é escolher um e usar sempre o mesmo no projeto.


5. Duas armadilhas: = e ;

if (x = 0): guardar no lugar de comparar

Você já viu esse erro em Operadores de Comparação: com um = só, o if guarda o valor na variável e testa o valor guardado. if (vidas = 0) zera as vidas e nunca entra no bloco; if (estoque = 5) muda o estoque para 5 e sempre entra. O gcc avisa com suggest parentheses around assignment used as truth value [-Wparentheses]. Toda vez que esse aviso aparecer, troque por ==.

if (…);: o ponto e vírgula que desliga o if

#include <stdio.h>

int main(void) {
    int temperatura = 25;
    if (temperatura > 30);
    {
        printf("Que calor! Ligue o ventilador.\n");
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra pontovirgula.c -o pontovirgula
pontovirgula.c: In function ‘main’:
pontovirgula.c:5:26: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
    5 |     if (temperatura > 30);
      |                          ^
pontovirgula.c:5:5: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    5 |     if (temperatura > 30);
      |     ^~
pontovirgula.c:6:5: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
    6 |     {
      |     ^
$ ./pontovirgula
Que calor! Ligue o ventilador.     « com 25 graus?!
  1. O ; sozinho é uma instrução vazia: "não faça nada".
  2. Então o if ficou assim: "se a temperatura passar de 30, não faça nada".
  3. O bloco { … } da linha de baixo virou um bloco comum, fora do if, e roda sempre.

O primeiro aviso diz "sugiro chaves em volta do corpo vazio de um if". Ele vem do -Wextra (-Wempty-body); só com -Wall, sobra apenas o aviso de recuo. No editor do guia, o Clang diz if statement has empty body ("o if tem corpo vazio").

Se houver um else, o mesmo ; vira erro de compilação, porque o else fica sem o if dele:

$ cat elsesolto.c
#include <stdio.h>

int main(void) {
    int nota = 4;
    if (nota >= 6);
        printf("Aprovada\n");
    else
        printf("Reprovada\n");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra elsesolto.c -o elsesolto
elsesolto.c: In function ‘main’:
elsesolto.c:5:19: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
    5 |     if (nota >= 6);
      |                   ^
elsesolto.c:5:5: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
    5 |     if (nota >= 6);
      |     ^~
elsesolto.c:6:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
    6 |         printf("Aprovada\n");
      |         ^~~~~~
elsesolto.c:7:5: error: ‘else’ without a previous ‘if’
    7 |     else
      |     ^~~~

O if terminou no ;, o printf("Aprovada\n"); é uma instrução solta, e o else chegou sem nenhum if logo antes: "else sem um if anterior". O Clang diz só error: expected expression. A causa, nos dois casos: o ; depois do ) do if.


6. Variáveis criadas dentro do bloco

Uma variável declarada dentro das chaves só existe ali dentro. Quando o bloco termina, ela some:

#include <stdio.h>

int main(void) {
    int pontos = 120;
    if (pontos > 100) {
        int bonus = 10;
        pontos = pontos + bonus;
    }
    printf("Pontos: %d, bônus: %d\n", pontos, bonus);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra escopo.c -o escopo
escopo.c: In function ‘main’:
escopo.c:9:47: error: ‘bonus’ undeclared (first use in this function)
    9 |     printf("Pontos: %d, bônus: %d\n", pontos, bonus);
      |                                               ^~~~~
escopo.c:9:47: note: each undeclared identifier is reported only once for each function it appears in

"bonus não declarada." Na linha 9, fora das chaves, bonus não existe mais. Já pontos, criada fora do if, existe nos dois lugares, e a mudança feita dentro do bloco continua valendo depois. Se você precisa do valor depois do if, declare a variável antes dele: int bonus = 0;. A aula Escopo explica as regras completas.


Formas diferentes de escrever a mesma decisão

FormaExemploQuando usar
if com chavesif (x < 0) { x = 0; }Sempre. É o padrão deste guia.
if sem chavesif (x < 0) x = 0;Funciona com uma instrução só, mas evite: é a origem do erro da seção 4.
if + elseif (n % 2 == 0) { … } else { … }Quando há exatamente dois caminhos.
Dois if separadosif (n % 2 == 0) { … }
if (n % 2 != 0) { … }
Evite para caminhos opostos: repete a pergunta e, se você errar uma das duas, os dois blocos podem rodar (ou nenhum). Use else.
Mais de dois caminhosif … else if … elseAula else if.
Escolher um valormaior = a > b ? a : b;Aula Operador Ternário.

Quando dá errado

warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]

O que significa: o recuo diz que uma linha está dentro do if, mas não está: faltam as chaves (ou sobrou um ;). No Clang: misleading indentation; statement is not part of the previous 'if'.

Como consertar: coloque { } em volta de todas as linhas que devem depender do if.

warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]

O que significa: há um ; logo depois do ) do if. O if não controla nada. Só aparece com -Wextra.

Como consertar: apague o ;: if (temperatura > 30) {.

error: ‘else’ without a previous ‘if’

O que significa: o else não está logo depois de um if (ou do bloco dele). Causas: um ; depois do if (…), ou um if sem chaves com duas linhas antes do else. No Clang: error: expected expression.

Como consertar: confira se não há ; depois do ) e use chaves: if (…) { … } else { … }.

warning: suggest parentheses around assignment used as truth value

O que significa: você escreveu if (x = valor), que guarda em vez de comparar.

Como consertar: if (x == valor).

error: ‘bonus’ undeclared (first use in this function) depois de um if

O que significa: a variável foi criada dentro das chaves do if e você a usou fora. No Clang: use of undeclared identifier 'bonus'.

Como consertar: declare a variável antes do if, já com um valor inicial.

error: expected ‘(’ before …
$ gcc -std=c23 -Wall -Wextra semparen.c -o semparen
semparen.c: In function ‘main’:
semparen.c:5:8: error: expected ‘(’ before ‘nota’
    5 |     if nota >= 6 {
      |        ^~~~
      |        (
semparen.c:5:8: warning: statement with no effect [-Wunused-value]
    5 |     if nota >= 6 {
      |        ^~~~

O que significa: faltaram os parênteses da condição. Em C eles são obrigatórios (em algumas outras linguagens, não). O gcc até mostra onde pôr o (. O aviso que vem depois ("instrução sem efeito") é consequência do primeiro erro, de um gcc já confuso: conserte o erro e ele some.

Como consertar: if (nota >= 6) {.


Referência rápida

EstruturaO que faz
if (cond) { A }Roda A só se cond for diferente de 0.
if (cond) { A } else { B }Roda A se cond for verdadeira; senão, roda B. Sempre exatamente um dos dois.
if (x) / if (!x)O mesmo que if (x != 0) / if (x == 0).
if (a > 0 && b > 0)Condição composta (Operadores Lógicos).
if (x = 0)Errado: guarda 0 em x. Use ==.
if (cond);Errado: o ; encerra o if. Tire o ;.

Exercícios

1. Complete o if

Complete para mostrar a mensagem só quando saldo for negativo:

(saldo < 0) { printf("Conta no vermelho\n"); }

2. Senão

Complete com a palavra que introduz o caminho alternativo:

if (nota >= 6) { printf("Aprovada\n"); } { printf("Reprovada\n"); }

3. A condição

Complete a condição para o bloco rodar quando vidas for igual a zero:

if (vidas 0) { printf("Fim de jogo\n"); }

4. O que este trecho mostra, com int x = 5;? if (x > 10) printf("A"); printf("B");

5. Por que if (n > 0); { printf("positivo"); } mostra "positivo" até com n = -3?

6. Qual opção do gcc liga o aviso -Wmisleading-indentation?

Missão final: o frete grátis

Missão: a lojinha da Ana dá frete grátis para compras de 100 reais ou mais; abaixo disso, o frete é 15 reais. O programa lê o valor da compra (a entrada preparada é 120.50). Complete com if e else: no caminho do frete grátis, mostre Frete grátis!; no outro, mostre Frete: R$ 15.00 e some 15 ao total. Sem avisos. Com 120.50, a saída deve ser:

Frete grátis!
Total: R$ 120.50
#include <stdio.h>

int main(void) {
    double compra = 0;
    if (scanf("%lf", &compra) != 1) {
        return 1;
    }
    double total = compra;

    // escreva o if e o else aqui

    printf("Total: R$ %.2f\n", total);
    return 0;
}
#include <stdio.h>

int main(void) {
    double compra = 0;
    if (scanf("%lf", &compra) != 1) {
        return 1;
    }
    double total = compra;

    if (compra >= 100) {
        printf("Frete grátis!\n");
    } else {
        printf("Frete: R$ 15.00\n");
        total = total + 15;
    }

    printf("Total: R$ %.2f\n", total);
    return 0;
}

Teste também com 99.90 (deve mostrar o frete e o total de 114.90) e com 100 (frete grátis: o "ou mais" pede >=, não >).


Resumo

  • if (condição) { … } roda o bloco só quando a condição é verdadeira (diferente de 0). Os parênteses são obrigatórios.
  • else { … } é o caminho alternativo: com if/else, exatamente um dos blocos roda. Depois, o programa continua normalmente.
  • Qualquer número serve de condição: 0 é falso, o resto é verdadeiro. if (x) = if (x != 0).
  • Chaves sempre. Sem elas, o if controla só a primeira instrução (-Wmisleading-indentation, do -Wall).
  • if (x = 0) guarda em vez de comparar (-Wparentheses). if (…); desliga o if (-Wempty-body, do -Wextra) e, com else, dá ‘else’ without a previous ‘if’.
  • Variáveis criadas dentro das chaves só existem ali dentro.