Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Decisões com condicionais

Condicionais fazem o programa escolher o que executar. O if avalia uma expressão entre parênteses e roda o bloco entre chaves só quando ela é verdadeira; o else dá o caminho alternativo, e else if encadeia novos testes. Numa cadeia, as condições são verificadas de cima para baixo e apenas o primeiro bloco verdadeiro é executado.

C Fundamentos Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
C / Fundamentos

Decisões com condicionais

por Tatum Winslow
Iniciante

Entenda o conceito

Condicionais fazem o programa escolher o que executar. O if avalia uma expressão entre parênteses e roda o bloco entre chaves só quando ela é verdadeira; o else dá o caminho alternativo, e else if encadeia novos testes. Numa cadeia, as condições são verificadas de cima para baixo e apenas o primeiro bloco verdadeiro é executado.

As condições usam operadores relacionais (==, !=, <, <=, >, >=) e lógicos: && (e), || (ou) e ! (não). O C não tinha um tipo booleano nas primeiras versões: uma comparação produz o int 1 ou 0, e qualquer valor diferente de zero conta como verdadeiro. Por isso if (ativo) funciona com um int, e por isso if (x = 0), com um = só, é um erro clássico que compila.

Quando um único valor inteiro, como um char, escolhe entre várias opções fixas, o switch organiza melhor: cada case é uma constante, o break encerra o bloco e o default cobre o resto. Para escolher entre dois valores numa expressão, há o operador ternário condição ? a : b.

↳ Pontos-chave
  • Numa cadeia if / else if / else, só o primeiro bloco com condição verdadeira é executado; coloque os casos mais específicos primeiro.
  • Compare com ==; um = só faz atribuição, e if (x = 0) compila, muda a variável e nunca entra no bloco.
  • Em C, 0 é falso e qualquer outro valor é verdadeiro; comparações produzem 1 ou 0 do tipo int.
  • && e || fazem curto-circuito: a segunda parte só é avaliada se a primeira não decidir o resultado.
  • No switch, cada case aceita só constantes inteiras e precisa de break; sem ele, a execução continua no case seguinte.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01Frete da loja com if, else if e else

frete.c
/* frete.c: if, else if e else para escolher o frete */
#include <stdio.h>

int main(void)
{
    int total = 180;      /* valor do carrinho em reais */
    int distancia = 35;   /* km até o endereço de entrega */
    int frete;

    /* As condições são testadas de cima para baixo; vale a primeira verdadeira */
    if (total >= 200) {
        frete = 0;
    } else if (distancia <= 10) {
        frete = 8;
    } else if (distancia <= 50) {
        frete = 15;
    } else {
        frete = 30;
    }

    printf("Carrinho: R$ %d\n", total);
    printf("Distância: %d km\n", distancia);
    printf("Frete: R$ %d\n", frete);

    /* if sozinho, sem else: o bloco só roda quando a condição é verdadeira */
    if (frete == 0) {
        puts("Frete grátis!");
    }
    if (total < 200) {
        printf("Faltam R$ %d para o frete grátis.\n", 200 - total);
    }
    return 0;
}

A cadeia escolhe um único valor de frete: com total igual a 180, o primeiro teste falha; distancia <= 10 também falha; distancia <= 50 é verdadeiro, então o frete é 15 e os testes seguintes nem são avaliados. O else final cobre qualquer distância acima de 50 km.

Os dois if do fim são independentes, sem else: cada um decide sozinho se imprime algo. Aqui só o segundo é verdadeiro e o programa mostra que faltam R$ 20 para o frete grátis.

02Regras de empréstimo na biblioteca com &&, || e !

emprestimo.c
/* emprestimo.c: operadores relacionais e lógicos (&&, ||, !) */
#include <stdio.h>

int main(void)
{
    int emprestados = 2;
    int limite = 3;
    int dias_em_atraso = 0;
    int carteirinha_ativa = 1;  /* em C, 0 é falso e qualquer outro valor é verdadeiro */
    int reservado = 0;

    /* && exige que todas as condições sejam verdadeiras */
    if (carteirinha_ativa && emprestados < limite && dias_em_atraso == 0) {
        puts("Empréstimo liberado.");
    } else {
        puts("Empréstimo bloqueado.");
    }

    /* || basta que uma seja verdadeira; ! inverte o valor lógico */
    if (!carteirinha_ativa || dias_em_atraso > 0) {
        puts("Procure o balcão para regularizar.");
    } else {
        puts("Cadastro em dia.");
    }

    /* Renova só se o livro não estiver reservado por outra pessoa */
    if (!reservado) {
        puts("Renovação disponível.");
    }

    /* Uma comparação produz um int: 1 (verdadeiro) ou 0 (falso) */
    printf("emprestados < limite vale %d\n", emprestados < limite);
    printf("dias_em_atraso > 0 vale %d\n", dias_em_atraso > 0);
    return 0;
}

O empréstimo só é liberado se as três condições ligadas por && forem verdadeiras ao mesmo tempo. Já o aviso de regularização usa ||: basta a carteirinha estar inativa ou haver atraso. O ! inverte um valor lógico, então !reservado é verdadeiro quando reservado vale 0.

As duas últimas linhas mostram o que uma comparação devolve em C: um int que vale 1 quando é verdadeira e 0 quando é falsa. É por isso que variáveis int como carteirinha_ativa podem ser usadas direto como condição.

03Categoria do produto com switch e operador ternário

categoria.c
/* categoria.c: switch, break, casos agrupados e operador ternário */
#include <stdio.h>

int main(void)
{
    char categoria = 'e';   /* L = livro, E = eletrônico, A = alimento */
    int preco = 120;
    int desconto_pct;

    /* switch compara um valor inteiro (char também é) com constantes */
    switch (categoria) {
    case 'L':
    case 'l':               /* casos agrupados: maiúscula ou minúscula */
        puts("Categoria: livro");
        desconto_pct = 10;
        break;              /* sem break, a execução seguiria para o próximo case */
    case 'E':
    case 'e':
        puts("Categoria: eletrônico");
        desconto_pct = 5;
        break;
    case 'A':
    case 'a':
        puts("Categoria: alimento");
        desconto_pct = 0;
        break;
    default:                /* nenhum case combinou */
        puts("Categoria desconhecida");
        desconto_pct = 0;
        break;
    }

    int desconto = preco * desconto_pct / 100;
    int final = preco - desconto;
    printf("Preço: R$ %d | Desconto: R$ %d\n", preco, desconto);

    /* Ternário: condição ? valor_se_verdadeiro : valor_se_falso */
    printf("Total: R$ %d (%s)\n", final, desconto > 0 ? "com desconto" : "preço cheio");
    return 0;
}

O switch compara categoria com cada case e começa a executar no que combinar. Dois case seguidos sem código entre eles compartilham o mesmo bloco, o que aceita a letra maiúscula e a minúscula. O break encerra o switch; sem ele, a execução continuaria pelos blocos de baixo. Com 'e', o desconto é de 5%, ou R$ 6 sobre R$ 120.

Na última linha, o ternário desconto > 0 ? "com desconto" : "preço cheio" escolhe qual texto vai para o %s. Ele é uma expressão, não um comando, por isso cabe dentro do argumento do printf.


Agora é com você

Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.

01Situação de um produto no estoqueFácil

Uma caneca térmica tem 4 unidades em estoque e o mínimo aceitável é 5. Mostre os dados e classifique o produto como esgotado, com estoque baixo ou disponível.

↳ Requisitos
  • Guardar a quantidade e o mínimo em variáveis int.
  • Usar uma única cadeia if / else if / else para escolher a situação.
  • Considerar esgotado quando a quantidade for 0, estoque baixo quando for menor que o mínimo e disponível nos demais casos.
código inicial
#include <stdio.h>

int main(void)
{
    int quantidade = 4;
    int minimo = 5;

    puts("Produto: Caneca térmica");
    /* Imprima a quantidade e o mínimo */

    /* Escolha a situação com if, else if e else */

    return 0;
}
saída esperada
Produto: Caneca térmica
Quantidade: 4
Mínimo: 5
Situação: estoque baixo
Ver dica

O valor 0 também é menor que o mínimo. Pense em qual teste precisa vir primeiro para ele não cair na faixa errada.

Ver resposta
#include <stdio.h>

int main(void)
{
    int quantidade = 4;
    int minimo = 5;

    puts("Produto: Caneca térmica");
    printf("Quantidade: %d\n", quantidade);
    printf("Mínimo: %d\n", minimo);

    /* O caso mais restrito vem primeiro */
    if (quantidade == 0) {
        puts("Situação: esgotado");
    } else if (quantidade < minimo) {  /* só chega aqui se não for 0 */
        puts("Situação: estoque baixo");
    } else {
        puts("Situação: disponível");
    }
    return 0;
}

Passo a passo

  1. quantidade e minimo são contagens, então ficam em int; o programa imprime os dois antes de decidir, para a saída mostrar os dados usados.
  2. O primeiro teste, quantidade == 0, usa == (comparação), e não = (atribuição). Se for verdadeiro, imprime "esgotado" e pula o resto da cadeia.
  3. O else if (quantidade < minimo) só é avaliado quando o primeiro teste falhou, por isso não precisa repetir quantidade > 0.
  4. Com 4 < 5 verdadeiro, o programa imprime "estoque baixo" e não olha mais nada.
  5. O else final não tem condição: ele cobre todos os casos que sobraram (quantidade maior ou igual ao mínimo).

Por que funciona

Uma cadeia if / else if / else executa um único bloco: o da primeira condição verdadeira, de cima para baixo. Colocando o caso mais específico (zero) antes do mais geral (abaixo do mínimo), cada faixa de valores cai em exatamente uma mensagem, sem sobreposição.

Erros comuns

  • = no lugar de ==: if (quantidade = 0) atribui 0 à variável e a condição vale 0 (falso); o produto nunca aparece como esgotado e a quantidade é perdida. O GCC com -Wall avisa; use ==.
  • Ordem invertida: testar quantidade < minimo primeiro faz o 0 cair em "estoque baixo", porque 0 também é menor que 5. Teste o zero antes.
  • Três if independentes: sem os else, um valor pode imprimir duas situações (0 é "esgotado" e também "estoque baixo"). Encadeie com else if.

Outra forma de resolver

Dá para guardar o texto numa variável e imprimir uma vez só:

const char *situacao = "disponível";
if (quantidade == 0) {
    situacao = "esgotado";
} else if (quantidade < minimo) {
    situacao = "estoque baixo";
}
printf("Situação: %s\n", situacao);

É preferível quando a situação será usada em mais de um lugar, como num relatório e num alerta.

Saída esperada

Produto: Caneca térmica
Quantidade: 4
Mínimo: 5
Situação: estoque baixo

02Multa por atraso na bibliotecaMédio

Um livro foi devolvido com 9 dias de atraso por um assinante. Há 2 dias de carência; depois, a multa é de R$ 2 por dia, e assinantes pagam metade. Calcule a multa e diga se um novo empréstimo está liberado: ele é bloqueado com mais de 7 dias de atraso para quem não assina, ou com mais de 14 dias para qualquer leitor.

↳ Requisitos
  • Usar um if aninhado para aplicar o desconto do assinante só quando houver multa.
  • Mostrar sim ou não para o assinante com o operador ternário.
  • Escrever a regra de bloqueio numa única condição com &&, || e !.
código inicial
#include <stdio.h>

int main(void)
{
    int dias_atraso = 9;
    int assinante = 1;      /* 1 = sim, 0 = não */
    int multa = 0;

    /* Calcule a multa com a carência e o desconto */

    /* Imprima os dados e a situação do próximo empréstimo */

    return 0;
}
saída esperada
Dias de atraso: 9
Assinante: sim
Multa: R$ 7
Novo empréstimo: liberado
Ver dica

Escreva a regra de bloqueio em português primeiro e troque cada "e", "ou" e "não" pelo operador correspondente; use parênteses para agrupar o "e".

Ver resposta
#include <stdio.h>

int main(void)
{
    int dias_atraso = 9;
    int assinante = 1;      /* 1 = sim, 0 = não */
    int multa = 0;

    /* Até 2 dias de carência; depois, R$ 2 por dia */
    if (dias_atraso > 2) {
        multa = (dias_atraso - 2) * 2;
        if (assinante) {
            multa = multa / 2;  /* assinante paga metade */
        }
    }

    printf("Dias de atraso: %d\n", dias_atraso);
    printf("Assinante: %s\n", assinante ? "sim" : "não");
    printf("Multa: R$ %d\n", multa);

    /* Bloqueia: mais de 7 dias sem assinatura, ou mais de 14 em qualquer caso */
    if ((dias_atraso > 7 && !assinante) || dias_atraso > 14) {
        puts("Novo empréstimo: bloqueado");
    } else {
        puts("Novo empréstimo: liberado");
    }
    return 0;
}

Passo a passo

  1. assinante é um int usado como valor lógico: em C, 0 é falso e qualquer outro valor é verdadeiro, então if (assinante) basta.
  2. O if (dias_atraso > 2) aplica a carência: com 9 dias, sobram 7 dias cobrados, e a multa vale 7 * 2 = 14.
  3. O if aninhado só roda dentro do primeiro: para assinantes, 14 / 2 dá 7. A divisão é inteira, o que serve aqui porque a multa é cobrada em reais inteiros.
  4. O ternário assinante ? "sim" : "não" escolhe a string direto no argumento do printf, sem precisar de um if só para isso.
  5. A condição de bloqueio junta &&, ! e ||. Os parênteses deixam claro que o && forma um grupo: (9 > 7 && !1) é falso e 9 > 14 também, então o empréstimo é liberado.

Por que funciona

Operadores lógicos combinam comparações simples numa regra só: && exige as duas partes, || aceita qualquer uma e ! inverte. Eles também fazem curto-circuito: se dias_atraso > 7 for falso, o C nem avalia !assinante, porque o && já é falso. O if aninhado expressa uma regra que só faz sentido dentro de outra (o desconto só existe se houver multa).

Erros comuns

  • Esquecer a carência: multa = dias_atraso * 2; cobra os 9 dias e dá R$ 9 para o assinante. Desconte os 2 dias antes de multiplicar.
  • & e | no lugar de && e ||: são operadores bit a bit; às vezes dão o mesmo resultado por acaso, mas não fazem curto-circuito e quebram com valores diferentes de 0 e 1. Use os duplos.
  • ! aplicado à comparação inteira: !dias_atraso > 7 é lido como (!dias_atraso) > 7, que é sempre falso. Para negar uma comparação, escreva !(dias_atraso > 7) ou dias_atraso <= 7.

Outra forma de resolver

O desconto do assinante também pode ser um ternário:

multa = assinante ? multa / 2 : multa;

Fica mais curto, mas o if aninhado é mais fácil de ler e de estender quando a regra ganha outras faixas de desconto.

Saída esperada

Dias de atraso: 9
Assinante: sim
Multa: R$ 7
Novo empréstimo: liberado

03Cupom de desconto no carrinhoDesafio

Um carrinho de R$ 250 recebeu o cupom 'v'. Use um switch para tratar os cupons P (10% com compra mínima de R$ 100), V (R$ 30 com compra mínima de R$ 150) e F (frete grátis), aceitando maiúsculas e minúsculas, e calcule o frete: grátis pelo cupom ou quando o valor com desconto for de pelo menos R$ 200; caso contrário, R$ 20.

↳ Requisitos
  • Escolher o cupom com switch, agrupando os case da letra maiúscula e da minúscula e com default para cupom inválido.
  • Conferir a compra mínima com um if dentro do case e imprimir a mensagem do cupom (aplicado ou o valor exigido).
  • Calcular o frete com o operador ternário e uma condição com ||.
  • Imprimir as linhas na ordem da saída esperada, com o total igual ao valor com desconto mais o frete.
código inicial
#include <stdio.h>

int main(void)
{
    int subtotal = 250;
    char cupom = 'v';
    int desconto = 0;
    int frete_gratis = 0;

    printf("Subtotal: R$ %d\n", subtotal);

    /* Trate o cupom com switch */

    /* Calcule o frete e o total */

    return 0;
}
saída esperada
Subtotal: R$ 250
Cupom VALOR aplicado
Desconto: R$ 30
Frete: grátis
Total a pagar: R$ 220
Ver dica

case só aceita constantes, então a compra mínima não pode ir no rótulo do case; ela é um if dentro do bloco.

Ver resposta
#include <stdio.h>

int main(void)
{
    int subtotal = 250;
    char cupom = 'v';
    int desconto = 0;
    int frete_gratis = 0;

    printf("Subtotal: R$ %d\n", subtotal);

    switch (cupom) {
    case 'P':
    case 'p':                       /* maiúscula ou minúscula */
        if (subtotal >= 100) {
            desconto = subtotal * 10 / 100;
            puts("Cupom PERCENTUAL aplicado");
        } else {
            puts("Cupom exige R$ 100 em compras");
        }
        break;
    case 'V':
    case 'v':
        if (subtotal >= 150) {
            desconto = 30;
            puts("Cupom VALOR aplicado");
        } else {
            puts("Cupom exige R$ 150 em compras");
        }
        break;
    case 'F':
    case 'f':
        frete_gratis = 1;
        puts("Cupom FRETE aplicado");
        break;
    default:
        puts("Cupom inválido");
        break;
    }

    int com_desconto = subtotal - desconto;
    /* Frete grátis pelo cupom ou por compra de R$ 200 após o desconto */
    int frete = (frete_gratis || com_desconto >= 200) ? 0 : 20;

    printf("Desconto: R$ %d\n", desconto);
    if (frete == 0) {
        puts("Frete: grátis");
    } else {
        printf("Frete: R$ %d\n", frete);
    }
    printf("Total a pagar: R$ %d\n", com_desconto + frete);
    return 0;
}

Passo a passo

  1. O subtotal é impresso primeiro, e só depois o switch decide o cupom; assim a mensagem do cupom aparece na segunda linha.
  2. switch (cupom) compara o char com cada case. Os pares case 'V': e case 'v': sem nada entre eles fazem as duas letras caírem no mesmo bloco.
  3. Dentro de cada case, um if confere o valor mínimo da compra: o cupom pode existir e mesmo assim não valer para este carrinho.
  4. O break no fim de cada bloco encerra o switch; sem ele, o cupom V continuaria executando o bloco do F e daria frete grátis por engano.
  5. O default trata qualquer letra não prevista, para o programa nunca ficar em silêncio.
  6. O ternário calcula o frete numa linha: (0 || 220 >= 200) é verdadeiro, então frete vale 0.
  7. O if final escolhe entre "grátis" e o valor, e o total soma o valor com desconto e o frete: 220 + 0.

Por que funciona

O switch é a ferramenta certa quando um único valor inteiro (um char, aqui) escolhe entre várias opções fixas: cada case é um ponto de entrada e o break é a saída. Já as regras com faixas e combinações (>=, ||) ficam com if e com o ternário, porque case só aceita constantes, não comparações como subtotal >= 150.

Juntando os dois, cada parte da regra de negócio fica onde é mais legível: o tipo do cupom no switch, as condições de valor nos if e a escolha simples entre dois números no ternário.

Erros comuns

  • Esquecer o break: sem ele no case 'v', a execução "cai" no case 'f', imprime "Cupom FRETE aplicado" também e liga frete_gratis. O GCC com -Wimplicit-fallthrough avisa; coloque break em todo case.
  • Comparação no case: case subtotal >= 150: não compila (case label does not reduce to an integer constant). Faixas de valores vão num if dentro do case.
  • Aspas duplas no case: case "v": também não compila, porque "v" é uma string, não um char. Use 'v'.
  • Frete calculado sobre o subtotal: subtotal >= 200 daria frete grátis mesmo se o desconto levasse a compra para R$ 190. A regra pede o valor depois do desconto.

Outra forma de resolver

Sem switch, a escolha do cupom vira uma cadeia de if / else if:

if (cupom == 'V' || cupom == 'v') {
    /* ... */
} else if (cupom == 'P' || cupom == 'p') {
    /* ... */
}

Funciona igual e é preferível quando as condições não são só igualdade com constantes; com várias letras fixas, o switch deixa as opções alinhadas e mais fáceis de conferir.

Saída esperada

Subtotal: R$ 250
Cupom VALOR aplicado
Desconto: R$ 30
Frete: grátis
Total a pagar: R$ 220

Últimas lições

Ver o arquivo completo →