Pular para conteúdo

Semana 03 — Estruturas de repetição

Nesta semana

Unidade 1 · Seg 24/08 · Qua 26/08 · Sex 28/08

Computadores são incansáveis: fazem a mesma coisa milhões de vezes sem reclamar. Nesta semana você aprende a aproveitar isso com as estruturas de repetição (os laços ou loops): while, do-while e for. Em vez de escrever a mesma instrução cem vezes, você a escreve uma vez e diz ao computador quantas vezes (ou até quando) repeti-la.

Junto com as condicionais da semana passada, os laços completam o controle de fluxo — e, a partir daqui, você já consegue resolver uma quantidade enorme de problemas. Digite e execute cada exemplo.

🎯 Objetivos de aprendizagem

Ao final desta semana, você deverá ser capaz de:

  • identificar os três ingredientes de todo laço (inicialização, condição, atualização) e por que a ausência de qualquer um causa um laço infinito;
  • escrever laços com while, do-while e for e escolher o mais adequado a cada problema;
  • implementar os padrões fundamentais de contador e acumulador;
  • ler dados até uma condição de parada (padrão sentinela);
  • controlar a execução com break e continue;
  • construir e rastrear laços aninhados, base para tabelas e (na Unidade 2) matrizes.

📖 Segunda — Estudo do conteúdo

1. Por que repetir?

Imagine escrever um programa para somar as notas de 100 alunos, ou imprimir a tabuada do 7, ou pedir uma senha até o usuário acertar. Sem repetição, você teria que copiar e colar a mesma instrução dezenas de vezes — inviável, e impossível quando o número de repetições só é conhecido durante a execução.

Um laço executa um bloco de instruções repetidamente, enquanto uma condição for verdadeira. É um dos recursos mais poderosos da programação.

flowchart TD
    A["condição verdadeira?"] -->|sim| B["executa o corpo do laço"]
    B --> A
    A -->|não| C["sai do laço e continua"]

2. A anatomia de um laço: três ingredientes

Todo laço bem-comportado tem três elementos. Se algum falta ou está errado, o laço nunca termina (laço infinito) ou nem começa:

  1. Inicialização — preparar a variável de controle (ex.: i = 1).
  2. Condição de continuação — o teste que decide se repete (ex.: i <= 5).
  3. Atualização — modificar a variável a cada volta, para que a condição um dia fique falsa (ex.: i++).

Cada execução completa do corpo é chamada de iteração.

Laço infinito: o pesadelo do iniciante

Se você esquecer a atualização, a condição nunca muda e o programa trava:

int i = 1;
while (i <= 5) {
    printf("%d\n", i);
    // faltou o i++  ->  i é sempre 1  ->  repete para sempre!
}
Se isso acontecer, interrompa o programa com Ctrl + C no terminal (ou feche a janela no Code::Blocks). Depois, procure o ingrediente que faltou.

3. while — repita enquanto (teste no início)

O while testa a condição antes de cada iteração. Se já for falsa de saída, o corpo não executa nenhuma vez.

int i = 1;                 // 1. inicialização
while (i <= 5) {           // 2. condição
    printf("%d ", i);
    i++;                   // 3. atualização
}
// Saída: 1 2 3 4 5

Use while quando você não sabe de antemão quantas vezes vai repetir — a repetição depende de uma condição que muda durante a execução.

4. do-while — execute e depois teste (teste no fim)

O do-while executa o corpo primeiro e só então avalia a condição. Consequência: o corpo roda pelo menos uma vez, mesmo que a condição já comece falsa.

int opcao;
do {
    printf("Digite um numero positivo: ");
    scanf("%d", &opcao);
} while (opcao <= 0);       // repete enquanto a entrada for inválida

É a escolha ideal para menus e validação de entrada: você precisa pedir o dado ao menos uma vez antes de poder testá-lo.

O ; obrigatório do do-while

Diferentemente das outras estruturas, o do-while termina com ponto e vírgula depois da condição: } while (opcao <= 0);. Esquecê-lo é erro de compilação.

5. for — o laço de contagem

O for reúne os três ingredientes em uma única linha, na ordem for (inicialização; condição; atualização). É a forma preferida quando você sabe quantas vezes vai repetir.

for (int i = 1; i <= 5; i++) {
    printf("%d ", i);
}
// Saída: 1 2 3 4 5

Ordem de execução do for:

flowchart TD
    I["inicialização (uma vez)"] --> C{"condição?"}
    C -->|verdadeira| B["corpo do laço"]
    B --> U["atualização"]
    U --> C
    C -->|falsa| F["fim"]

O for acima é equivalente ao while da seção 3 — apenas mais compacto e com os três ingredientes juntinhos, o que facilita a leitura e reduz o risco de esquecer a atualização.

Variando o passo e a direção

O for não precisa contar de 1 em 1, nem para cima:

for (int i = 0; i <= 20; i += 5) { ... }   // 0, 5, 10, 15, 20
for (int i = 10; i >= 1; i--) { ... }      // contagem regressiva: 10..1

Curiosidade 💡 — declarar i dentro do for

Em C moderno (padrão C99 em diante, que usamos), você pode declarar a variável de controle dentro do for: for (int i = 0; ...). Assim, i só existe dentro do laço (seu escopo é o laço). Isso é uma boa prática: evita reaproveitar i sem querer em outra parte do programa. Falaremos de escopo em detalhe na Semana 04.

6. Qual laço usar?

Os três laços são intercambiáveis (tudo que se faz com um dá para fazer com os outros), mas cada um tem seu ponto forte:

Use… Quando… Exemplo típico
for você sabe o número de repetições percorrer de 1 a N, tabuada
while repete enquanto uma condição valer, número de vezes desconhecido ler até um valor sentinela
do-while o corpo precisa rodar ao menos uma vez menus, validação de entrada

7. Padrões fundamentais: contador e acumulador

Dois padrões aparecem em praticamente todo programa com laço:

  • Contador: uma variável que conta ocorrências (soma 1 quando algo acontece).
  • Acumulador: uma variável que acumula um total (soma valores, multiplica, etc.).

Ambos precisam ser inicializados antes do laço:

soma_e_media.c
#include <stdio.h>

int main(void) {
    int n;
    printf("Quantos numeros? ");
    scanf("%d", &n);

    int soma = 0;               // acumulador (começa em 0)
    for (int i = 0; i < n; i++) {
        int valor;
        scanf("%d", &valor);
        soma += valor;          // acumula
    }

    double media = (double) soma / n;   // cast evita divisão inteira!
    printf("Soma = %d, media = %.2f\n", soma, media);
    return 0;
}

Inicialize acumuladores com o valor 'neutro'

  • Para somar, comece em 0 (soma = 0): somar 0 não muda nada.
  • Para multiplicar (ex.: fatorial), comece em 1 (produto = 1): multiplicar por 1 não muda nada. Começar um produto em 0 daria sempre 0 — erro clássico!

8. O padrão sentinela: repetir até um valor de parada

Às vezes não sabemos quantos dados virão — repetimos até o usuário digitar um valor especial (a sentinela) que sinaliza o fim.

sentinela.c
#include <stdio.h>

int main(void) {
    int valor, soma = 0, quantidade = 0;

    printf("Digite numeros (0 para encerrar):\n");
    scanf("%d", &valor);
    while (valor != 0) {           // 0 é a sentinela
        soma += valor;
        quantidade++;
        scanf("%d", &valor);       // lê o próximo antes de testar de novo
    }

    if (quantidade > 0) {
        printf("Soma = %d, media = %.2f\n", soma, (double) soma / quantidade);
    } else {
        printf("Nenhum numero informado.\n");
    }
    return 0;
}

Repare no padrão "ler antes, testar, e reler no fim do corpo" — é o que garante que a sentinela em si não entre na conta.

9. break e continue: desviando o fluxo do laço

  • breakabandona o laço imediatamente, seguindo para depois dele.
  • continuepula o resto do corpo e vai direto para a próxima iteração (no for, executa a atualização antes de testar de novo).
for (int i = 1; i <= 10; i++) {
    if (i % 2 == 0) {
        continue;      // pula os pares: nada abaixo executa nesta iteração
    }
    if (i > 7) {
        break;         // ao chegar em 9, abandona o laço
    }
    printf("%d ", i);  // imprime: 1 3 5 7
}

Use com parcimônia

break e continue são úteis, mas em excesso tornam o laço difícil de seguir. Muitas vezes uma condição bem escrita (ou um if dentro do corpo) deixa a intenção mais clara do que um break no meio.

Curiosidade 💡 — laços intencionalmente infinitos

Às vezes queremos um laço "sem fim" que só termina por um break interno (por exemplo, o laço principal de um jogo ou de um menu). Dois idiomas comuns:

while (1) { ... if (sair) break; ... }   // 1 é sempre verdadeiro
for (;;)  { ... if (sair) break; ... }   // for com os três campos vazios
Ambos são "infinitos por construção"; o break é a única saída.

10. Laços aninhados

Um laço pode conter outro laço. Para cada iteração do laço externo, o laço interno roda por completo. São indispensáveis para trabalhar com estruturas bidimensionais (tabelas hoje; matrizes na Semana 09).

tabuada.c
#include <stdio.h>

int main(void) {
    for (int i = 1; i <= 3; i++) {          // externo: cada tabuada
        printf("Tabuada do %d:\n", i);
        for (int j = 1; j <= 10; j++) {     // interno: as 10 linhas
            printf("  %d x %d = %d\n", i, j, i * j);
        }
        printf("\n");
    }
    return 0;
}

Se o externo roda 3 vezes e o interno 10, o printf de dentro executa 3 × 10 = 30 vezes. Note que usamos variáveis de controle diferentes (i e j) — confundi-las é um erro comum.

Desenhando com laços aninhados

Um clássico para fixar a ideia — imprimir um triângulo:

for (int lin = 1; lin <= 4; lin++) {
    for (int col = 1; col <= lin; col++) {  // o interno depende do externo!
        printf("*");
    }
    printf("\n");
}
// Saída:
// *
// **
// ***
// ****
Aqui o número de * de cada linha depende de lin — o laço interno vai até lin.

11. Erros comuns desta semana (resumão)

Checklist de armadilhas

  • Laço infinito por esquecer a atualização da variável de controle.
  • Erro de contagem por um (off-by-one): usar <= onde era <, ou começar em 1 onde era 0. Confira sempre o primeiro e o último valor.
  • Esquecer o ; no fim do do-while.
  • Colocar ; logo após for (...) ou while (...) (corpo vazio — como no if).
  • Inicializar o acumulador com o valor errado (produto começando em 0).
  • Deixar a sentinela entrar na conta (soma/contagem).
  • Reutilizar a mesma variável em laços aninhados (i no lugar de j).

👥 Quarta — Encontro

Chegue tendo estudado e executado os exemplos. No encontro vamos:

  1. Traduzir um problema em laço: identificar inicialização, condição e atualização.
  2. Depurar, ao vivo, um laço infinito e um erro off-by-one.
  3. Construir juntos um programa com laço aninhado.

Problema-guia — estatísticas com sentinela: ler vários números até o usuário digitar -1; ao final, informar quantos foram lidos, a soma, a média e o maior deles.

estatisticas.c
#include <stdio.h>

int main(void) {
    int valor, soma = 0, quantidade = 0, maior = 0;

    printf("Digite numeros (-1 para encerrar):\n");
    scanf("%d", &valor);
    while (valor != -1) {
        soma += valor;
        quantidade++;
        if (quantidade == 1 || valor > maior) {  // o 1º valor sempre vira o maior inicial
            maior = valor;
        }
        scanf("%d", &valor);
    }

    if (quantidade > 0) {
        printf("Qtd=%d Soma=%d Media=%.2f Maior=%d\n",
               quantidade, soma, (double) soma / quantidade, maior);
    } else {
        printf("Nenhum numero informado.\n");
    }
    return 0;
}

Para pensar antes: por que a condição do "maior" precisa do quantidade == 1? O que aconteceria se inicializássemos maior = 0 e todos os números fossem negativos?


✍️ Sexta — Estudo dirigido

Exercícios guiados (Beecrowd)

Resolva, em C, na ordem sugerida:

Problema O que exercita
1059 Números Pares for simples
1060 Números Positivos contador dentro do laço
1064 Positivos e Média contador + acumulador
1065 Impares entre dois números classificação em laço
1070 Seis Ímpares for com passo
1072 Calculando e Somando laço com condicional dentro
1073 Quadrado dos Pares expressão dentro do laço
1078 Tabuada laço de contagem
1080 Maior e Posição encontrar máximo com laço
1116 Dividindo Valores validação (do-while)
1132 Multiples of 13 acumulador condicional

Desafios para escrever do zero

  1. Múltiplos: imprimir todos os números de 1 a 100 que sejam múltiplos de 3 ou 5.
  2. Fatorial: ler n e calcular n! com um laço (cuidado: acumulador começa em 1).
  3. É primo? ler um número e testar se é primo (verifique divisores de 2 até n-1).
  4. Contagem regressiva: ler n e imprimir de n até 1, e depois "Fim!".
  5. Soma dos dígitos: ler um inteiro e somar seus dígitos (dica: % 10 pega o último dígito, / 10 remove-o; repita com um while).
  6. Tabela de conversão: imprimir uma tabela de Celsius (0 a 100, de 10 em 10) para Fahrenheit, bem alinhada com % de largura.
  7. Pirâmide: com laços aninhados, imprimir um triângulo de números:
    1
    1 2
    1 2 3
    1 2 3 4
    

Para investigar (curiosidade prática)

  • Escreva um for (int i = 0; i < 5; i++) e outro for (int i = 0; i <= 5; i++). Quantas vezes cada um executa? Relacione com o erro off-by-one.
  • Rode um laço while (1) que imprime e não tem break. Interrompa com Ctrl + C e reflita sobre o que aconteceu.

✅ Checklist de autoavaliação

  • Reconheço os três ingredientes de um laço e sei por que um laço fica infinito.
  • Escrevo while, do-while e for e escolho o adequado a cada situação.
  • Uso contadores e acumuladores, inicializando-os corretamente.
  • Implemento o padrão sentinela sem contar o valor de parada.
  • Sei usar break e continue conscientemente.
  • Consigo escrever e rastrear um laço aninhado, prevendo quantas vezes o corpo roda.
  • Evito o erro off-by-one conferindo o primeiro e o último valor.

🔗 Referências e para se aprofundar

  • Material 2026.1 — Aula 03: Estruturas de Repetição.
  • BACKES, André. Linguagem C: completa e descomplicada (estruturas de repetição).
  • KERNIGHAN, B.; RITCHIE, D. The C Programming Language, cap. 3.
  • MIZRAHI, Victorine V. Treinamento em Linguagem C (comandos de repetição).
  • Referência: cppreference — for · while.
  • Prática: Beecrowd.

Antes de seguir para a Semana 04

Você já tem os três pilares da lógica: sequência, decisão e repetição. Na próxima semana vamos aprender a organizar tudo isso em funções — blocos reutilizáveis que deixam programas grandes gerenciáveis. É o que fecha a Unidade 1.