Iniciante⏱️ 35 min

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:

  1. O ciclo while (com avaliação de condição no início / pré-teste).
  2. O ciclo do ... while (com avaliação de condição no fim / pós-teste).
  3. O ciclo for (controlo completo com inicialização, teste e passo).
  4. A equivalência formal entre for e while.
  5. Prevenção de ciclos infinitos e boas práticas.
  6. 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 i com o valor 11?
    O programa imprime os números de 11 a 20.
  • E se inicializares i com o valor 25?
    Como a condição 25 <= 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 if ou while simples, a instrução do ... while termina 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 i com o valor 25?
    1. O programa entra no bloco do e imprime 25.
    2. Incrementa i para 26.
    3. No final, avalia 26 <= 20, o que é falso.
    4. O ciclo termina.
      Conclusão: Enquanto o while executou 0 vezes, o do ... while executou 1 vez!

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:

  1. inicializações: Executadas uma única vez no arranque do ciclo (ex.: i = 1).
  2. 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).
  3. pós-instrução: Executada no fim de cada repetição, antes do próximo teste (habitualmente um incremento ou decremento, ex.: i++ ou n += 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 for quando o ciclo tem um contador bem definido e um passo de incremento constante.
  • Usa while quando 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 do for (1 <= 0) é logo falsa e o programa devolve corretamente fatorial = 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 / contador garante 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 while avalia a condição no início (executa 0 ou mais vezes).
  • O ciclo do ... while avalia 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 um while.
  • É responsabilidade do programador assegurar que o corpo do ciclo altera o estado das variáveis para evitar ciclos infinitos.