5

Condicionais

Até este ponto, os programas que você desenvolveu seguem uma estrutura estritamente sequencial, na qual cada instrução é executada logo após a anterior, do início ao fim, sem que nenhum comando seja ignorado. Entretanto, na programação do mundo real, frequentemente você encontrará situações em que um bloco de comandos deve ser executado apenas se uma determinada condição for atendida. Para lidar com esses cenários, utilizamos as estruturas de seleção ou comandos de controle condicional, que permitem ao software "tomar decisões" e selecionar qual caminho seguir.

Esse conceito é análogo ao funcionamento de um fluxograma, no qual o símbolo de um losango representa um ponto de decisão. Dependendo da análise de uma condição do tipo verdadeiro ou falso, o fluxo do programa pode divergir para caminhos distintos, conforme ilustrado na imagem abaixo:

figura41startcondA > B?start->condsimcond->simSimnaocond->naoNão

Este capítulo foi projetado para que você compreenda o funcionamento de cada uma das estruturas de seleção disponíveis na linguagem C. Ao final da leitura, você será capaz de:

  • Definir condições lógicas de forma precisa.
  • Utilizar o comando if para execuções condicionais simples.
  • Aplicar o comando else para tratar caminhos alternativos.
  • Criar estruturas de aninhamento condicional para problemas complexos.
  • Fazer uso do operador ternário para simplificações de código.
  • Utilizar o comando switch para seleções múltiplas.
  • Compreender a importância do comando break no controle do fluxo.
Nota

As estruturas condicionais são os pilares que conferem inteligência ao código, permitindo que o programa reaja de forma diferente a diferentes entradas de dados do usuário ou estados do sistema.

Definindo uma Condição5.1

Uma condição é qualquer expressão relacional que utilize operadores de comparação como >, <, >=, <=, == ou != e que resulte em uma resposta do tipo verdadeiro ou falso. Para ilustrar, considere a condição $x > 0$:

  • Se o valor de $x$ for positivo, a condição será considerada verdadeira.
  • Se o valor de $x$ for igual a zero ou negativo, a condição será considerada falsa.

Ampliando esse conceito, uma expressão condicional é qualquer construção que resulte em um valor lógico, podendo ser elaborada através da combinação de diferentes tipos de operadores:

  • Matemáticos: +, -, *, /, %.
  • Relacionais: >, <, >=, <=, ==, !=.
  • Lógicos: &&, ||.

O uso conjunto desses operadores permite que você crie lógicas mais sofisticadas para o seu programa. Por exemplo, para verificar se a divisão de $x$ por 2 é maior do que o valor de $y$ subtraído de 3, utiliza-se a expressão: $x / 2 > y - 3$.

Abaixo, você pode observar outros exemplos de como estruturar essas expressões:

Objetivo da condiçãoExpressão em C
$x$ é maior ou igual a $y$?x >= y
$x$ é maior do que $y + 2$?x > y + 2
$x - 5$ é diferente de $y + 3$?x - 5 != y + 3
$x$ é maior do que $y$ e menor do que $z$?(x > y) && (x < z)

Ao avaliar uma condição, o compilador busca um valor de retorno para fundamentar uma decisão. É fundamental que você compreenda que, em linguagem C, uma expressão condicional não precisa seguir o formato convencional de comparação; uma variável isolada pode atuar como uma condição ao retornar seu próprio valor.

Para entender esse comportamento, lembre-se de que o computador opera internamente com o sistema binário:

  • Se uma condição é falsa, o computador atribui a ela o valor zero.
  • Se uma condição é verdadeira, o computador atribui a ela qualquer valor diferente de zero.

Portanto, o valor de uma variável inteira pode ser interpretado diretamente como o resultado de uma lógica condicional: se a variável for igual a zero, a condição é tratada como falsa; se for diferente de zero, será tratada como verdadeira.

Confira algumas equivalências que o compilador utiliza durante essa avaliação:

  • Para verificar se uma variável é verdadeira (diferente de zero): A expressão (num != 0) é equivalente a utilizar apenas (num), pois a variável sozinha retorna um valor que o computador interpreta como verdadeiro se não for nulo.
  • Para verificar se uma variável é falsa (igual a zero): A expressão (num == 0) é equivalente a utilizar a negação (!num), pois se a variável for zero, sua negação retornará um valor considerado verdadeiro pelo sistema.

Comando IF5.2

Na linguagem C, você utilizará o comando if sempre que precisar escolher entre dois caminhos dentro do seu programa ou quando desejar que a execução de certas instruções dependa do resultado de um teste lógico.

A estrutura geral do comando if é a seguinte:

if(condição) {
    sequência de comandos;
}

Durante a execução, a condição entre parênteses é avaliada pelo sistema:

  • Se a condição for verdadeira, o bloco de comandos interno será executado integralmente.
  • Se a condição for falsa, a sequência de comandos dentro do if será ignorada, e o programa seguirá seu fluxo a partir da primeira instrução após o fechamento do bloco.

Observe abaixo um exemplo prático de um programa que solicita um número inteiro e informa ao usuário se ele é maior do que 10:

#include <stdio.h>
#include <stdlib.h>

int main(){
    int num;
    printf("Digite um numero: ");
    scanf("%d", &num);

    if(num > 10) {
        printf("O numero e maior do que 10\n");
    }

    system("pause");
    return 0;
}

Neste código, a mensagem de confirmação só aparecerá se a condição estipulada for atendida. Caso contrário, nada será exibido. Para visualizar melhor como as instruções são processadas passo a passo, podemos observar a representação em fluxograma na imagem a seguir:

FlowchartInícioInícioDeclVarint num;Início->DeclVarFimFimPrintInprintf("Digite um numero: ");DeclVar->PrintInScanscanf("%d", &num);PrintIn->ScanDecisãoif (num > 10)Scan->DecisãoPrintOutprintf("O numero e maior que 10");Decisão->PrintOutSIMPausesystem("pause");Decisão->PauseNÃOPrintOut->PauseReturnreturn 0;Pause->ReturnReturn->Fim
Atenção ao Ponto e Vírgula

Diferente da maioria das instruções em C, você nunca deve utilizar o ponto e vírgula (;) logo após a condição do comando if.

Veja este exemplo de erro comum:

if(num > 10); // ERRADO!
printf("O numero e maior do que 10\n");

Como o ponto e vírgula serve para finalizar uma instrução, ao colocá-lo após o if, você indica ao compilador que o comando condicional termina ali mesmo, sem executar nada. Isso faz com que o printf seguinte seja tratado como um comando comum, fora do controle do if, sendo exibido independentemente do valor de num. O compilador não acusará erro de sintaxe, mas a lógica do seu programa estará comprometida.

Uso das Chaves { }5.2.1

Você deve utilizar as chaves ({ }) para delimitar um bloco de instruções. Por padrão, comandos de condição ou repetição em C afetam apenas a instrução imediatamente seguinte a eles.

Portanto, se você precisar que o if controle duas ou mais linhas de código, é obrigatório envolvê-las em chaves:

if(condição) {
    comando 1;
    comando 2;
    // ... outros comandos
}
Dica de Estilo

Se o seu if possuir apenas um comando, as chaves tornam-se opcionais. Entretanto, utilizá-las mesmo em comandos únicos é uma boa prática que ajuda a evitar erros futuros caso você decida adicionar mais instruções ao bloco.

if(num > 10)
    printf("O numero e maior que 10\n");

/* Forma equivalente utilizando chaves */
if(num > 10) {
    printf("O numero e maior que 10\n");
}

Comando ELSE5.2.2

O comando else deve ser compreendido como um complemento direto do comando if. Ele atua auxiliando o if na tarefa de selecionar entre diferentes caminhos a serem seguidos pelo fluxo do programa.

O uso do else é opcional, e sua sequência de instruções só será processada se o valor da condição testada pelo if for FALSA. Em resumo, enquanto o if define o que deve ser feito quando uma condição é verdadeira, o else gerencia o que deve ocorrer quando ela é falsa.

Esta lógica de caminhos alternativos é visualizada claramente em um fluxograma, onde a decisão bifurca o fluxo em dois blocos distintos:

figura41startcondA > B?start->condsimcond->simIFnaocond->naoELSE
  • Com o comando if sozinho: se a condição for verdadeira, o bloco é executado; se for falsa, o programa apenas segue seu fluxo padrão, ignorando o bloco condicional.
  • Com a estrutura if-else: se a condição for verdadeira, a primeira sequência de comandos (bloco if) é executada; se for falsa, a segunda sequência de comandos (bloco else) é obrigatoriamente executada.

Veja abaixo um exemplo de programa que verifica se um número inteiro fornecido pelo usuário é igual ou diferente de 10:

#include <stdio.h>
#include <stdlib.h>

int main(){
    int num;
    printf("Digite um numero: ");
    scanf("%d", &num);

    if(num == 10){
        printf("O numero e igual a 10.\n");
    } else {
        printf("O numero e diferente de 10.\n");
    }

    system("pause");
    return 0;
}

Para facilitar a compreensão do processo de execução passo a passo deste exemplo, o fluxograma abaixo ilustra como o computador percorre as instruções:

FlowchartFIGURA 4.6PrintInprintf ("Digite um numero: ");Scanscanf ("%d", &num);PrintIn->ScanDecisaoif(num == 10){Scan->DecisaoPrintSimprintf ("O numero e igual a 10.\n");Decisao->PrintSimSIMPrintNaoprintf ("O numero e diferente de 10.\n");Decisao->PrintNaoNÃOPausesystem ("pause");PrintSim->PausePrintNao->Pause
O ELSE não possui condição própria

Um erro comum é tentar atribuir uma condição ao else. Como ele representa o "caso contrário" exato do if, ele não necessita (e não aceita) uma nova expressão lógica.

if(num == 10){
    printf("O numero e igual a 10.\n");
} else(num != 10){ // ERRO! O else não recebe condição.
    printf("O numero e diferente de 10.\n");
}

Assim como ocorre no if, você não deve utilizar o ponto e vírgula (;) logo após a palavra else. Caso faça isso, o compilador entenderá que o bloco do else está vazio, executando o comando seguinte como se ele estivesse fora da estrutura condicional.

Independência de Blocos e Uso de Chaves5.2.3

As sequências de comandos do if e do else são independentes entre si. Se o bloco if for acionado, o bloco else será automaticamente descartado naquela execução, e vice-versa. Por esse motivo, cada um deve possuir seu próprio conjunto de chaves ({ }) para definir seu escopo de atuação.

Estrutura CorretaEstrutura Errada
if(condicao) { comandos; }if(condicao) { comandos;
else { comandos; }else comandos; }

Assim como no comando if, as chaves podem ser omitidas se houver apenas um único comando dentro do bloco else.

Aninhamento de IF5.3

Um if aninhado ocorre quando você utiliza um comando if dentro do bloco de comandos de outro if (ou else) mais externo. Essencialmente, trata-se de uma estrutura condicional inserida dentro de outra para refinar a tomada de decisão.

A estrutura geral de um aninhamento segue este padrão:

if(condição 1) {
    sequência de comandos;
    if(condição 2) {
        sequência de comandos;
    } else {
        sequência de comandos;
    }
} else {
    sequência de comandos;
}

Nessa estrutura, o programa inicia testando a condição 1. Se o resultado for verdadeiro (diferente de zero), o bloco interno associado é executado; caso contrário, o programa desvia para o else correspondente, se este existir. Esse processo de verificação se repete para cada novo if encontrado pelo fluxo de execução.

O aninhamento é extremamente útil quando o programa precisa lidar com mais de dois caminhos possíveis. Por exemplo, enquanto um if simples pode determinar se um número é maior que outro, ele sozinho não consegue distinguir entre três estados: se o número é maior, menor ou igual.

Considere o exemplo prático abaixo:

#include <stdio.h>
#include <stdlib.h>

int main(){
    int num;
    printf("Digite um numero: ");
    scanf("%d", &num);

    if(num == 10){
        printf("O numero e igual a 10.\n");
    } else {
        if(num > 10)
            printf("O numero e maior que 10.\n");
        else
            printf("O numero e menor que 10.\n");
    }

    system("pause");
    return 0;
}

A visualização desse processo através de um fluxograma torna a lógica de múltiplos caminhos muito mais evidente:

FlowchartScanscanf ("%d", &num);Decisao1if(num == 10){Scan->Decisao1PrintIgualprintf ("O numero e igual a 10.\n");Decisao1->PrintIgualSIMDecisao2if (num > 10)Decisao1->Decisao2NÃOPausesystem ("pause");PrintIgual->PausePrintMaiorprintf ("O numero e maior que 10.");Decisao2->PrintMaiorSIMPrintMenorprintf ("O numero e menor que 10.");Decisao2->PrintMenorNÃOPrintMaior->PausePrintMenor->Pause
Cuidado com a Associação do ELSE

Um ponto crítico no aninhamento é identificar a qual if um else está vinculado. Em C, um else é sempre associado ao if mais próximo anterior a ele que esteja dentro do mesmo bloco de comandos.

Observe este exemplo de ambiguidade visual:

if(cond1)
    if(cond2)
        comando_A;
else
    comando_B;

Embora o else pareça alinhado ao primeiro if, o compilador o associará ao segundo if. Para forçar a associação ao primeiro, você deve isolar o if interno com chaves:

if(cond1) {
    if(cond2)
        comando_A;
} else
    comando_B; // Agora associado ao primeiro if
Importante

Não existe "aninhamento de elses" isolados. Como o else representa o caso contrário de um teste, cada um deve obrigatoriamente possuir um if correspondente anterior, embora nem todo if precise de um else. Tentar declarar um else sem um if prévio resultará em erro de compilação.

Operador Ternário (?)5.4

O operador ? é amplamente conhecido como operador ternário. Ele funciona como uma simplificação prática da estrutura if-else, permitindo realizar testes lógicos em uma única linha de comando, em vez de utilizar blocos de instruções completos.

A sintaxe geral do operador ternário é:

expressão condicional ? expressão1 : expressão2;

A lógica de funcionamento é idêntica à do if-else: a expressão condicional é avaliada primeiro. Se o resultado for verdadeiro, o valor da expressão1 é retornado; caso seja falso, o valor da expressão2 torna-se o resultado da operação.

Aplicações Práticas5.4.1

Embora possa ser utilizado de diversas formas, o operador ? é tipicamente empregado para atribuições condicionais. No exemplo abaixo, você pode comparar como o operador ternário reduz drasticamente a verbosidade do código ao encontrar o maior entre dois números:


// Usando a estrutura if-else tradicional
#include <stdio.h>
#include <stdlib.h>

int main() {
    int x, y, z;

    printf("Digite x: ");
    scanf("%d", &x);
    printf("Digite y: ");
    scanf("%d", &y);

    // Estrutura tradicional
    if (x > y) {
        z = x;
    } else {
        z = y;
    }

    printf("Maior = %d\n", z);
    system("pause");
    return 0;
}

// Usando o Operador Ternário
#include <stdio.h>
#include <stdlib.h>

int main() {
    int x, y, z;

    printf("Digite x: ");
    scanf("%d", &x);
    printf("Digite y: ");
    scanf("%d", &y);

    // Atribuição com operador ternário
    z = (x > y) ? x : y;

    printf("Maior = %d\n", z);
    system("pause");
    return 0;
}

Você também pode utilizar o operador ternário para criar lógicas compactas, como um contador circular. Neste caso, uma variável é incrementada até um limite e reiniciada ao atingir o valor máximo:

index = (index == 3) ? 0 : ++index;
Flexibilidade do Operador

Apesar de ser mais limitado que o if-else, o operador ternário não se restringe apenas a atribuições. Você pode utilizá-lo para executar funções diferentes, como o printf, dependendo de uma condição:

(num == 10) ? printf("Igual a 10\n") : printf("Diferente de 10\n");

Embora seja uma ferramenta poderosa para simplificar expressões complicadas, lembre-se de que o uso excessivo pode prejudicar a legibilidade do código em lógicas muito extensas.

Comando SWITCH5.5

Além dos comandos if e else, a linguagem C oferece o switch, um comando de seleção múltipla. Ele funciona de maneira semelhante ao aninhamento if-else-if, mas com aplicações mais específicas e limitadas. Enquanto o if permite testar expressões lógicas e relacionais complexas, o switch apenas verifica se uma variável (obrigatoriamente do tipo int ou char) é igual a determinados valores constantes. A estrutura geral do comando switch é apresentada a seguir:

switch (variável) {
    case valor1:
        sequência de comandos;
        break;
    case valor2:
        sequência de comandos;
        break;
    // ...
    case valorN:
        sequência de comandos;
        break;
    default:
        sequência de comandos;
}

Este comando é ideal para testar uma única variável em relação a diversos valores preestabelecidos. Durante a execução, o valor da variável é comparado sequencialmente com cada case; quando uma correspondência é encontrada, o respectivo bloco de comandos é executado. Confira o exemplo de um programa que identifica símbolos de pontuação:

#include <stdio.h>
#include <stdlib.h>

int main(){
    char ch;
    printf("Digite um simbolo de pontuacao: ");
    ch = getchar();

    switch( ch ) {
        case '.': printf("Ponto.\n"); break;
        case ',': printf("Virgula.\n"); break;
        case ':': printf("Dois pontos.\n"); break;
        case ';': printf("Ponto e virgula.\n"); break;
        default : printf("Nao eh pontuacao.\n");
    }

    system("pause");
    return 0;
}

O fluxo de execução desse exemplo pode ser visualizado na imagem abaixo:

FlowchartInicioInícioGetCharch = getchar();Inicio->GetCharFimFimSwitchswitch(ch) {GetChar->SwitchIgual1Igual?Switch->Igual1:nIgual2Igual?Igual1->Igual2NÃOCase1case '.': printf("Ponto.\n"); break;Igual1->Case1SIMIgual3Igual?Igual2->Igual3NÃOCase2case ',': printf("Virgula.\n"); break;Igual2->Case2SIMIgual4Igual?Igual3->Igual4NÃOCase3case ':': printf("Dois pontos.\n"); break;Igual3->Case3SIMCase4case ';': printf("Ponto e virgula.\n"); break;Igual4->Case4SIMDefaultdefault : printf("Nao eh pontuacao.\n");Igual4->DefaultNÃOCase1->FimCase2->FimCase3->FimCase4->FimDefault->Fim
O comando DEFAULT

O uso do default é opcional. Ele serve como uma cláusula de escape: sua sequência de comandos só será executada se a variável testada não for igual a nenhum dos valores definidos nos comandos case.

Embora o exemplo anterior possa ser reproduzido com estruturas if-else-if, o switch oferece uma solução muito mais elegante e legível para comparar o valor de uma única variável.

Uso do comando BREAK no switch5.5.1

A principal diferença entre o switch e o aninhamento if-else-if reside no uso do comando break. Quando o valor de um case corresponde à variável, o programa executa os comandos até encontrar um break. Se o break for omitido, o programa continuará executando todos os comandos dos cases seguintes, independentemente de seus valores. Observe o impacto da ausência do break no fluxograma a seguir:

FlowchartInicioInícioGetCharch = getchar();Inicio->GetCharFimFimSwitchswitch(ch) {GetChar->SwitchIgual1Igual?Switch->Igual1Igual2Igual?Igual1->Igual2NÃOCase1case '.': printf("Ponto.\n");Igual1->Case1SIMIgual3Igual?Igual2->Igual3NÃOCase2case ',': printf("Virgula.\n");Igual2->Case2SIMIgual4Igual?Igual3->Igual4NÃOCase3case ':': printf("Dois pontos.\n");Igual3->Case3SIMCase4case ';': printf("Ponto e virgula.\n");Igual4->Case4SIMDefaultdefault : printf("Nao eh pontuacao.\n");Igual4->DefaultNÃOCase1->Case2Case2->Case3Case3->Case4Case4->DefaultDefault->Fim

Embora o break seja quase sempre necessário para garantir a execução de apenas um bloco, sua ausência pode ser estratégica em casos como contagens regressivas ou quando múltiplos estados compartilham a mesma saída.

Uso das Chaves { } no case5.5.2

Geralmente, você não precisa utilizar chaves dentro de um case. No entanto, se o primeiro comando de um case for a declaração de uma variável, o uso das chaves torna-se obrigatório.

case '+': {
    int resultado = a + b; // Declaração exige chaves
    printf("Soma: %d\n", resultado);
}
break;

Isso ocorre porque a linguagem C proíbe que um salto condicional (como o que ocorre ao entrar em um switch) pule a declaração de uma variável dentro do mesmo escopo. Ao utilizar as chaves, você cria um novo escopo local para aquela variável, permitindo que ela seja "pulada" com segurança se aquele case não for o selecionado.

Questões5.6

1. A avaliação de condições em C baseia-se em valores numéricos. Considerando que o computador interpreta 0 como falso e qualquer outro valor como verdadeiro, analise o trecho abaixo e determine o que será impresso. Justifique sua resposta explicando como o if avalia a expressão (x = 0).

int main() {
    int x = 10;
    
    // Atenção: operador de atribuição '=' e não de igualdade '=='
    if (x = 2) {
        printf("Verdadeiro\n");
    } else {
        printf("Falso\n");
    }
    
    printf("Valor de x: %d", x);
    return 0;
}

2. O comando switch é uma alternativa elegante ao uso de múltiplos if-else encadeados, mas possui limitações. Assinale a alternativa que descreve corretamente uma restrição do switch em C:

  • A) Pode avaliar expressões lógicas complexas, como case (x > 10 && x < 20):.
  • B) Só aceita variáveis do tipo float ou double como parâmetro de controle.
  • C) Exige o uso obrigatório de chaves { } em cada case, mesmo que não haja declaração de variáveis.
  • D) Só pode testar igualdade de valores constantes do tipo int ou char.

3. O operador ternário é uma ferramenta poderosa para simplificação de código. Reescreva o trecho de código abaixo utilizando uma única linha com o operador ternário:

// Código Original
if (nota >= 60) {
    printf("Aprovado");
} else {
    printf("Reprovado");
}

4. Escreva um programa que leia um número inteiro fornecido pelo usuário. O programa deve verificar se o número é par ou ímpar e imprimir uma mensagem correspondente.

  • Entrada: Um número inteiro (ex: 7).
  • Saída Esperada: "O numero 7 eh impar."

5. Crie um programa que receba três números inteiros como entrada e imprima o maior deles. Utilize estruturas if-else aninhadas para realizar as comparações.

  • Entrada: Três números inteiros (ex: 10, 5, 20).
  • Saída Esperada: "O maior numero eh: 20".

6. Desenvolva uma calculadora básica utilizando o comando switch. O programa deve ler dois números reais e um operador aritmético (+, -, *, /).

  • Entrada: Dois números e um caractere (ex: 5.0, 2.0, '/').
  • Saída Esperada: O resultado da operação (ex: "Resultado: 2.50").
  • Requisito: Trate a divisão por zero, exibindo uma mensagem de erro caso o divisor seja 0.

7. Escreva um programa que leia um ano (ex: 2024) e determine se ele é bissexto ou não. As regras para um ano ser bissexto são:

  1. Ser divisível por 4.
  2. Não ser divisível por 100, a menos que seja também divisível por 400.
  • Exemplos:
    • 2000 é bissexto (divisível por 400).
    • 1900 não é bissexto (divisível por 100, mas não por 400).
    • 2024 é bissexto (divisível por 4 e não por 100).
  • Entrada: Um ano (inteiro).
  • Saída Esperada: "Bissexto" ou "Nao Bissexto".

8. Analise o comportamento do break no comando switch. Dado o código abaixo, qual será a saída exata se o usuário digitar o valor 2? Explique por que a saída ocorre dessa forma.

int main() {
    int op;
    scanf("%d", &op);
    
    switch(op) {
        case 1: printf("A");
        case 2: printf("B");
        case 3: printf("C");
        default: printf("D");
    }
    return 0;
}

Próximos passos5.7

No próximo capítulo, Estruturas de Repetição, deixaremos de executar o código apenas uma vez. Você aprenderá a criar loops (laços) utilizando os comandos while, do-while e for, permitindo que seu programa repita blocos de instruções quantas vezes forem necessárias para processar grandes volumes de dados ou realizar iterações controladas.