06. Instruções de Controlo de Fluxo: Repetição
Aprende as estruturas de repetição em C: ciclo while (pré-teste), ciclo do...while (pós-teste), ciclo for e respetiva equivalência, prevenção de ciclos infinitos e exercícios práticos com soluções interativas.
Instruções de Controlo de Fluxo: Repetição
Nas aulas anteriores aprendemos a executar sequências de instruções e a tomar decisões com if/else e switch. Contudo, um dos maiores trunfos da computação reside na capacidade de repetir um bloco de instruções dezenas, milhares ou milhões de vezes com rigor e velocidade estonteantes.
Nesta aula — a segunda parte do estudo sobre Controlo de Fluxo —, com base na apresentação Aula 5b — Instruções de Controlo de Fluxo: Repetição, vamos explorar os três tipos fundamentais de ciclos na linguagem C:
- O ciclo
while(com avaliação de condição no início / pré-teste). - O ciclo
do ... while(com avaliação de condição no fim / pós-teste). - O ciclo
for(controlo completo com inicialização, teste e passo). - A equivalência formal entre
forewhile. - Prevenção de ciclos infinitos e boas práticas.
- Resolução de exercícios práticos com soluções interativas ocultas.
1. O que são Estruturas de Repetição?
Uma estrutura de repetição (habitualmente designada por ciclo ou loop) permite executar um trecho de código repetidamente enquanto uma determinada condição de paragem permanecer verdadeira.
Na linguagem C podemos escolher entre três estruturas de repetição:
| Estrutura | Momento de Avaliação | Execuções Mínimas | Aplicação Típica |
|---|---|---|---|
while |
No início do ciclo (pré-teste) | 0 vezes | Repetições baseadas numa condição geral em que pode não ser necessário executar nenhuma vez. |
do ... while |
No fim do ciclo (pós-teste) | 1 vez | Menus interativos e validação de entrada de dados (onde o código tem de correr pelo menos uma vez). |
for |
No início, com passo no fim | 0 vezes | Contagens conhecidas ou incrementos regulares de variáveis de controlo. |
2. O Ciclo while
O ciclo while avalia a sua condição lógica antes de cada iteração. Se a condição for falsa logo à partida, o bloco de código nunca chega a ser executado.
Sintaxe
while (condição)
instrução;
Se o corpo do ciclo for constituído por mais do que uma instrução, é obrigatório usar chavetas { ... }:
while (condição) {
instrução1;
instrução2;
}
⚠️ Atenção ao Perigo de Ciclo Infinito:
O programador deve garantir que, dentro do corpo do ciclo, existe pelo menos uma instrução que altere o estado de uma variável de modo a tornar a condição falsa a dada altura. Caso contrário, o programa ficará preso num ciclo infinito até ser forçado a parar!
Exemplo: Contagem de 1 a 20
#include <stdio.h>
int main(void) {
int i = 1;
while (i <= 20) {
printf("%d\n", i);
i++; // Incrementa i para avançar em direção à condição de paragem
}
return 0;
}
Questões de Raciocínio (Slide 3):
- O que acontece se inicializares
icom o valor11?
O programa imprime os números de11a20. - E se inicializares
icom o valor25?
Como a condição25 <= 20é falsa logo no primeiro teste, o ciclo não executa nenhuma vez e o programa termina de imediato sem imprimir nada no terminal.
3. O Ciclo do ... while
O ciclo do ... while inverte a ordem: o bloco de instruções é executado primeiro e a condição lógica é testada apenas no final de cada iteração.
Sintaxe
do {
instruções;
} while (condição); // Não esquecer o ponto e vírgula obrigatório no final!
- Garantia de Execução: O código interior é executado pelo menos uma vez, mesmo que a condição seja falsa logo no início!
- Ponto Crítico de Sintaxe: Ao contrário de
ifouwhilesimples, a instruçãodo ... whiletermina obrigatoriamente com ponto e vírgula (;) após a condição de fecho.
Exemplo: Contagem com do ... while
#include <stdio.h>
int main(void) {
int i = 1;
do {
printf("%d\n", i);
i++;
} while (i <= 20);
return 0;
}
A Diferença Prática para o while (Slide 5):
- O que acontece se inicializares
icom o valor25?- O programa entra no bloco
doe imprime25. - Incrementa
ipara26. - No final, avalia
26 <= 20, o que é falso. - O ciclo termina.
Conclusão: Enquanto owhileexecutou 0 vezes, odo ... whileexecutou 1 vez!
- O programa entra no bloco
4. O Ciclo for
O ciclo for é a estrutura de repetição mais compacta e expressiva em C. Embora seja habitualmente utilizado em contagens cujo número de repetições é conhecido, em C o for é uma estrutura condicional geral e adapta-se a qualquer situação.
Sintaxe
for (inicializações; condição; pós-instrução)
instrução;
As três secções separadas por ponto e vírgula funcionam da seguinte forma:
inicializações: Executadas uma única vez no arranque do ciclo (ex.:i = 1).condição: Avaliada antes de cada iteração. Se for verdadeira (não-zero), executa o corpo do ciclo; se for falsa (zero), o ciclo termina (ex.:i <= 20).pós-instrução: Executada no fim de cada repetição, antes do próximo teste (habitualmente um incremento ou decremento, ex.:i++oun += 5).
5. A Equivalência Formal entre for e while
Qualquer ciclo for pode ser reescrito com um ciclo while, e vice-versa. As duas porções de código abaixo são rigorosamente equivalentes:
// Forma com for:
for (inicializações; condição; pós-instrução) {
instruções;
}
// Forma equivalente com while:
inicializações;
while (condição) {
instruções;
pós-instrução;
}
Isto significa que a escolha entre for e while é fundamentalmente uma questão de clareza e estilo de código:
- Usa
forquando o ciclo tem um contador bem definido e um passo de incremento constante. - Usa
whilequando a repetição depende de um evento externo (por exemplo, leitura de ficheiros ou valores centinela introduzidos pelo utilizador).
6. Exemplos de Aplicação da Apresentação
Exemplo 1: Contagem Simples de 1 a 20 (Slide 9)
#include <stdio.h>
int main(void) {
int i;
for (i = 1; i <= 20; i++) {
printf("%d\n", i);
}
return 0;
}
Exemplo 2: Quadrados de Números com Passo Composto (n += 5) (Slide 10)
O passo da pós-instrução não tem de ser apenas +1. Podemos incrementar em saltos arbitrários:
#include <stdio.h>
int main(void) {
int n, k;
for (n = 5; n <= 20; n += 5) {
k = n * n;
printf("O quadrado de %d e' %d\n", n, k);
}
return 0;
}
Saída Produzida no Ecrã:
O quadrado de 5 e' 25 (quando n = 5)
O quadrado de 10 e' 100 (quando n = 10)
O quadrado de 15 e' 225 (quando n = 15)
O quadrado de 20 e' 400 (quando n = 20)
(o ciclo termina quando n atinge 25, pois 25 <= 20 é falso)
Exemplo 3: Todos os Anos Bissextos do Milénio (Slide 11)
Um ano é bissexto se for divisível por 4 e não por 100, ou se for divisível por 400:
#include <stdio.h>
int main(void) {
int ano;
printf("--- ANOS BISSEXTOS DESTE MILÉNIO (2000 a 2999) ---\n");
for (ano = 2000; ano < 3000; ano += 4) {
if ((ano % 100 != 0) || (ano % 400 == 0)) {
printf("%d ", ano);
}
}
printf("\n");
return 0;
}
Exemplo 4: Mostrar Consoantes com Comparação for vs. while (Slide 12)
Podemos iterar diretamente sobre variáveis de tipo char porque caracteres são códigos numéricos ASCII:
Versão com ciclo for:
#include <stdio.h>
int main(void) {
char letra;
for (letra = 'a'; letra <= 'z'; letra++) {
// Se não for vogal, é consoante:
if (letra != 'a' && letra != 'e' && letra != 'i' && letra != 'o' && letra != 'u') {
// Imprime minúscula e maiúscula correspondente (letra - 'a' + 'A'):
printf("%c %c\n", letra, letra - 'a' + 'A');
}
}
return 0;
}
Versão equivalente com ciclo while:
#include <stdio.h>
int main(void) {
char letra = 'a'; // 1. Inicialização antes do ciclo
while (letra <= 'z') { // 2. Condição
if (letra != 'a' && letra != 'e' && letra != 'i' && letra != 'o' && letra != 'u') {
printf("%c %c\n", letra, letra - 'a' + 'A');
}
letra++; // 3. Pós-instrução manual no final do corpo
}
return 0;
}
Saída Produzida:
b B
c C
d D
...
z Z
7. Exercícios Práticos com Soluções Interativas
Resolve os exercícios por ti próprio antes de clicares para ver a solução!
Exercício 1: Validação de Entrada com do ... while
Enunciado: Escreve um programa que apresente um menu de opções numéricas (de 1 a 4) e insista repetidamente com o utilizador até que ele introduza uma opção estritamente válida (entre 1 e 4).
👉 Ver Solução Proposta
#include <stdio.h>
int main(void) {
int opcao;
do {
printf("\n=============================\n");
printf(" MENU PRINCIPAL \n");
printf("=============================\n");
printf("1. Ver Saldo da Conta\n");
printf("2. Depositar Dinheiro\n");
printf("3. Levantar Dinheiro\n");
printf("4. Sair do Programa\n");
printf("Escolha uma opcao (1-4): ");
scanf("%d", &opcao);
if (opcao < 1 || opcao > 4) {
printf("\n✗ Opção inválida! Por favor tenta novamente.\n");
}
} while (opcao < 1 || opcao > 4);
printf("\n✓ Escolheste a opção com sucesso: %d\n", opcao);
return 0;
}
Explicação:
- O bloco
doé executado sempre na primeira tentativa. - O teste
while (opcao < 1 || opcao > 4)força a repetição apenas se a opção digitada estiver fora do intervalo permitido.
Exercício 2: Cálculo de Fatorial com for
Enunciado: Escreve um programa que leia um número inteiro positivo $N$ e calcule o seu fatorial ($N! = N \times (N - 1) \times \dots \times 1$). Lembra-te de que por convenção matemática $0! = 1$.
👉 Ver Solução Proposta
#include <stdio.h>
int main(void) {
int n;
// Usamos unsigned long long porque fatoriais crescem a um ritmo gigantesco!
unsigned long long fatorial = 1;
printf("Digita um número inteiro (0 a 20): ");
if (scanf("%d", &n) != 1 || n < 0 || n > 20) {
printf("Entrada inválida! O número deve estar entre 0 e 20.\n");
return 1;
}
for (int i = 1; i <= n; i++) {
fatorial *= i; // Multiplica acumulando: fatorial = fatorial * i
}
printf("O fatorial de %d! é: %llu\n", n, fatorial);
return 0;
}
Explicação:
- Se o utilizador digitar
0, a condição dofor(1 <= 0) é logo falsa e o programa devolve corretamentefatorial = 1. - Para números maiores, multiplicamos cumulativamente através do operador de atribuição reduzida
*=.
Exercício 3: Somatório e Média de Números com Condição de Paragem (Flag / Centinela)
Enunciado: Cria um programa que leia uma série de números inteiros positivos introduzidos pelo utilizador, até que este introduza o número -1 (valor centinela de paragem). No final, o programa deve exibir quantos números foram introduzidos, a sua soma total e a média aritmética correspondente.
👉 Ver Solução Proposta
#include <stdio.h>
int main(void) {
int valor;
int contador = 0;
long soma = 0;
printf("Introduz números positivos (digita -1 para terminar):\n");
printf("Número: ");
scanf("%d", &valor);
while (valor != -1) {
if (valor >= 0) {
soma += valor;
contador++;
} else {
printf("Aviso: Apenas números positivos contam!\n");
}
printf("Número: ");
scanf("%d", &valor);
}
if (contador > 0) {
double media = (double)soma / contador;
printf("\n--- ESTATÍSTICAS FINAIS ---\n");
printf("Total de números lidos: %d\n", contador);
printf("Soma de todos os valores: %ld\n", soma);
printf("Média aritmética: %.2lf\n", media);
} else {
printf("\nNenhum número válido foi introduzido.\n");
}
return 0;
}
Explicação:
- O ciclo
whileé ideal quando a quantidade de repetições é desconhecida à partida e depende de uma ação do utilizador (o valor centinela-1). - O type casting
(double)soma / contadorgarante que a divisão calcula a média real e não truncada.
Resumo da Aula 06
- Os ciclos permitem repetir instruções com base numa condição de paragem.
- O ciclo
whileavalia a condição no início (executa 0 ou mais vezes). - O ciclo
do ... whileavalia a condição no fim e termina com;(executa 1 ou mais vezes, ideal para menus). - O ciclo
for (init; cond; pos)é compacto e equivale formalmente a umwhile. - É responsabilidade do programador assegurar que o corpo do ciclo altera o estado das variáveis para evitar ciclos infinitos.