05. Instruções de Controlo de Fluxo: Seleção
Domina as estruturas de tomada de decisão em C: a instrução if simples e completa, if's encadeados, o problema do 'else pendente' e a seleção múltipla com switch e break.
Instruções de Controlo de Fluxo: Seleção
Até ao momento, os nossos programas seguiam um fluxo linear: cada instrução era executada estritamente em sequência, uma a seguir à outra. Contudo, a capacidade de tomar decisões com base no valor de variáveis é o alicerce de qualquer algoritmo inteligente.
Nesta aula — a primeira parte do estudo sobre Controlo de Fluxo —, vamos explorar a fundo as instruções de seleção:
- A instrução de seleção simples e bilateral:
ifeif ... else. - Estruturas com decisões múltiplas:
if'sencadeados. - A ambiguidade clássica do “Else Pendente” (Dangling Else) e a sua resolução com blocos de chavetas
{}. - A instrução de seleção múltipla:
switch, a palavra-chavebreake o agrupamento de casos (fall-through). - Exercícios práticos interativos com soluções ocultas que podes revelar com um clique.
1. A Instrução de Seleção Simples: if … else
A instrução if permite condicionar a execução de uma instrução à verificação de uma dada condição lógica.
1. Forma Simples do if
Executa a instrução associada apenas se a condição for verdadeira (diferente de zero):
if (condição)
instrucao;
2. Forma Completa do if ... else
Permite bifurcar o fluxo de execução: se a condição for verdadeira executa instrucao1; se for falsa (igual a zero) executa instrucao2:
if (condição)
instrucao1;
else
instrucao2;
Regras Sintáticas Essenciais
- Parênteses Obrigatórios: Em C, a condição associada ao
iftem de estar obrigatoriamente entre parênteses:if (idade >= 18). - Instruções Simples vs. Compostas:
- Se precisares de executar apenas uma instrução, as chavetas são opcionais (mas recomendadas por clareza).
- Se precisares de executar duas ou mais instruções, deves agrupá-las obrigatoriamente num bloco de código delimitado por chavetas
{ ... }.
// Bloco de instruções compostas:
if (saldo >= preco) {
saldo = saldo - preco;
printf("Compra aprovada! Saldo restante: %.2f €\n", saldo);
} else {
printf("Saldo insuficiente para efetuar a compra!\n");
}
2. Exemplo Prático: Verificação de Maioridade
Eis a implementação clássica da verificação de maioridade legal:
#include <stdio.h>
int main(void) {
int idade;
printf("Digite a idade da pessoa: ");
if (scanf("%d", &idade) != 1) {
printf("Erro na leitura da idade!\n");
return 1;
}
if (idade >= 18) {
printf("Pessoa e' maior de idade.\n");
} else {
printf("Pessoa e' menor de idade.\n");
}
return 0;
}
3. Decisões Múltiplas com if's Encadeados
Quando um problema envolve mais de duas opções alternativas (por exemplo: determinar se um número é positivo, negativo ou nulo), encadeamos vários if's através da construção else if:
if (condição1)
instrucao1;
else if (condição2)
instrucao2;
else if (condição3)
instrucao3;
...
else
instrucaoN; // Executado se nenhuma das condições anteriores for satisfeita
Exemplo: Classificação de um Número
#include <stdio.h>
int main(void) {
int num;
printf("Digite um numero: ");
scanf("%d", &num);
if (num > 0) {
printf("O numero e' positivo.\n");
} else if (num < 0) {
printf("O numero e' negativo.\n");
} else {
printf("O numero e' igual a zero.\n");
}
return 0;
}
4. O Problema Clássico do “Else Pendente” (Dangling Else)
Considera o seguinte fragmento de código com múltiplos if's aninhados:
if (condição1)
if (condição2)
if (condição3)
instrucao1;
else
instrucao2;
else
instrucao3;
else
instrucao4;
À primeira vista, a indentação visual sugere a quem cada else pertence. Mas como é que o compilador de C resolve isto na ausência de chavetas?
⚠️ Regra Fundamental de C:
Oelseé sempre associado aoifmais próximo que ainda não tenha umelseassociado, independentemente da forma como o código está indentado no editor!
A Ambiguidade na Prática
Imagina este código:
// Código potencialmente enganoso:
if (condicao1)
if (condicao2)
instrucao1;
else
instrucao3; // A qual if pertence este else?
Mesmo que tenhas alinhado o else com o primeiro if (condicao1), o compilador vai associá-lo ao segundo if (condicao2)!
A Solução Definitiva: Usar Chavetas { ... }
Para contrariar a regra de associação por proximidade e garantir que o compilador entende exatamente a tua intenção lógica, usa sempre chavetas explicitamente:
// Solução inequívoca:
if (condicao1) {
if (condicao2) {
instrucao1;
}
} else {
// Este else pertence inequivocamente ao primeiro if!
instrucao3;
}
5. A Instrução de Seleção Múltipla: switch
Quando a seleção da instrução a executar depende da comparação de um valor de tipo ordinal (int ou char) com várias opções discretas constantes, a linguagem C disponibiliza uma estrutura muito mais limpa e legível: o switch.
Sintaxe Geral do switch
switch (seletor) {
case const1:
instrucoes1;
break;
case const2:
instrucoes2;
break;
...
case constN:
instrucoesN;
break;
default:
instrucoesPadrao;
}
Como Funciona o switch?
- O compilador avalia a expressão
seletor. - O valor é comparado sequencialmente com cada constante associada aos blocos
case. - Quando encontra uma constante coincidente, a execução do programa salta diretamente para essa linha e continua a executar.
- O bloco
defaulté opcional e é executado apenas quando o seletor não coincide com nenhum dos casos testados. - As constantes de cada
casetêm de ser todas diferentes entre si e devem ser valores constantes conhecidos em tempo de compilação (não podem ser variáveis ou intervalos comox > 10).
6. A Palavra-Chave break e o Efeito Fall-Through
A instrução break provoca a saída imediata do bloco switch, saltando para a instrução seguinte à chaveta de fecho }.
💥 Atenção ao Efeito Fall-Through:
Se omitires obreakno final de umcase, o programa continuará a executar o código docaseseguinte, mesmo que o valor não coincida!
Tirar Partido do Fall-Through: Agrupamento de Casos
Por vezes, queremos que várias opções diferentes executem o mesmo bloco de código. O C permite agrupar múltiplos case em cascata:
switch (seletor) {
case const1:
case const2:
instrucoesComuns_1_e_2;
break;
case const3:
case const4:
instrucoesComuns_3_e_4;
break;
default:
instrucoesDefault;
}
7. Exemplos Práticos de Aplicação do switch
Exemplo 1: Leitura do Estado Civil (char com maiúsculas e minúsculas)
Repara como agrupamos 's' e 'S' sem duplicar o código:
#include <stdio.h>
int main(void) {
char estado_civil;
printf("Qual o estado civil (S/C/V/D)? ");
scanf(" %c", &estado_civil);
switch (estado_civil) {
case 's':
case 'S':
printf("Solteiro(a)\n");
break;
case 'c':
case 'C':
printf("Casado(a)\n");
break;
case 'v':
case 'V':
printf("Viuvo(a)\n");
break;
case 'd':
case 'D':
printf("Divorciado(a)\n");
break;
default:
printf("Estado Civil Invalido!\n");
}
return 0;
}
Exemplo 2: Dia da Semana (Dias Úteis vs. Fim de Semana)
Neste exemplo, o seletor é um número inteiro de 1 a 7:
#include <stdio.h>
int main(void) {
int dia;
printf("Digite um valor de 1 a 7 (1=Domingo ... 7=Sábado): ");
scanf("%d", &dia);
switch (dia) {
case 1:
case 7:
printf("Fim de semana!\n");
break;
case 2:
case 3:
case 4:
case 5:
case 6:
printf("Dia de semana (dia de trabalho).\n");
break;
default:
printf("Valor invalido! Deve ser um número entre 1 e 7.\n");
}
return 0;
}
8. Exercícios Práticos com Soluções Interativas
Coloca os teus conhecimentos em prática! Tenta resolver os exercícios de forma autónoma antes de clicares para ver a solução.
Exercício 1: Classificador de Triângulos com Validação Geométrica
Enunciado: Escreve um programa que leia os três lados de um triângulo (a, b, c). O programa deve primeiro verificar se os lados formam de facto um triângulo válido (a soma de quaisquer dois lados tem de ser estritamente maior que o terceiro lado). Se for válido, classifica-o em:
- Equilátero: Todos os três lados iguais.
- Isósceles: Dois lados iguais e um diferente.
- Escaleno: Todos os lados diferentes entre si.
👉 Ver Solução Proposta
#include <stdio.h>
int main(void) {
float a, b, c;
printf("Introduz os três lados do triângulo (separados por espaço): ");
if (scanf("%f %f %f", &a, &b, &c) != 3 || a <= 0 || b <= 0 || c <= 0) {
printf("Entrada inválida! Os lados devem ser números positivos.\n");
return 1;
}
// 1. Validação da desigualdade triangular
if ((a + b > c) && (a + c > b) && (b + c > a)) {
// 2. Classificação
if (a == b && b == c) {
printf("Triângulo Equilátero (3 lados iguais).\n");
} else if (a == b || a == c || b == c) {
printf("Triângulo Isósceles (2 lados iguais).\n");
} else {
printf("Triângulo Escaleno (3 lados distintos).\n");
}
} else {
printf("As medidas introduzidas NÃO formam um triângulo válido!\n");
}
return 0;
}
Explicação:
- A condição
(a + b > c) && (a + c > b) && (b + c > a)garante a validade geométrica do triângulo. - Usamos
if ... else if ... elseencadeado para testar primeiro a igualdade tripla (a == b && b == c) e depois as igualdades parciais.
Exercício 2: Mini-Calculadora com switch
Enunciado: Cria uma calculadora que leia dois números reais (num1 e num2) e um caractere que represente a operação matemática pretendida ('+', '-', '*', '/'). Utiliza a instrução switch para calcular e apresentar o resultado da operação. No caso da divisão, garante que o divisor não é zero antes de calcular.
👉 Ver Solução Proposta
#include <stdio.h>
int main(void) {
double num1, num2, resultado;
char operacao;
printf("Introduz a expressão (exemplo: 12.5 + 4.2): ");
// O espaço antes de %c é crucial para descartar espaços ou quebras de linha anteriores
if (scanf("%lf %c %lf", &num1, &operacao, &num2) != 3) {
printf("Formato de entrada inválido!\n");
return 1;
}
switch (operacao) {
case '+':
resultado = num1 + num2;
printf("Resultado: %.2lf\n", resultado);
break;
case '-':
resultado = num1 - num2;
printf("Resultado: %.2lf\n", resultado);
break;
case '*':
resultado = num1 * num2;
printf("Resultado: %.2lf\n", resultado);
break;
case '/':
if (num2 != 0.0) {
resultado = num1 / num2;
printf("Resultado: %.4lf\n", resultado);
} else {
printf("Erro Matemático: Divisão por zero impossível!\n");
}
break;
default:
printf("Operador '%c' não reconhecido! Usa +, -, * ou /.\n", operacao);
}
return 0;
}
Explicação:
- O
switch (operacao)inspeciona diretamente o código ASCII do caractere introduzido. - Na divisão, um
if (num2 != 0.0)aninhado previne erros graves de divisão por zero. - Cada caso termina com
breakpara impedir a execução acidental dos casos seguintes.
Exercício 3: Conversor de Notas Qualitativas
Enunciado: Lê uma nota inteira entre 0 e 20 valores e atribui uma classificação descritiva de acordo com a seguinte escala:
- Menor que 10:
"Reprovado" - De 10 a 13:
"Suficiente" - 14 ou 15:
"Bom" - 16 ou 17:
"Muito Bom" - De 18 a 20:
"Excelente" - Qualquer outro valor:
"Nota Inválida"
👉 Ver Solução Proposta
#include <stdio.h>
int main(void) {
int nota;
printf("Introduz a nota do aluno (0 a 20): ");
if (scanf("%d", ¬a) != 1) {
printf("Entrada inválida!\n");
return 1;
}
if (nota < 0 || nota > 20) {
printf("Nota inválida! A nota tem de estar compreendida entre 0 e 20.\n");
} else if (nota < 10) {
printf("Classificação: Reprovado\n");
} else if (nota <= 13) {
printf("Classificação: Suficiente\n");
} else if (nota <= 15) {
printf("Classificação: Bom\n");
} else if (nota <= 17) {
printf("Classificação: Muito Bom\n");
} else {
printf("Classificação: Excelente\n");
}
return 0;
}
Explicação:
- Ao encadear os
else if, aproveitamos o facto de que cada teste já garante que a condição anterior era falsa (por exemplo, quando o código chega anota <= 13, já sabemos com certeza quenota >= 10).
Resumo da Aula 05
- A instrução
if (condição)bifurca a execução; as condições requerem obrigatoriamente parênteses. - Se um
ifouelsecontiver mais de uma instrução, deve ser delimitado por chavetas{ ... }. - O “Else Pendente” associa-se sempre ao
ifmais próximo que não tenhaelse; usam-se chavetas para alterar este comportamento. - O
switché a estrutura ideal para seleção baseada em valores inteiros ou caracteres (int/char). - A palavra-chave
breaké mandatória para evitar a continuação involuntária da execução (fall-through). - O agrupamento de múltiplos
caseconsecutivos permite partilhar o mesmo bloco de código entre várias condições.