A Arte da Repetição com while, do-while e for

[343] A Arte da Repetição com while, do-while e for

Três laços e um critério para escolher entre eles: for quando o número de voltas é conhecido, while quando a condição é incerta, do-while quando é preciso rodar pelo menos uma vez. Com os três ingredientes cuja falta gera laço infinito, break e continue, aninhamento e o padrão do acumulador.
Linguagem C

7 min de leitura

Um computador não se cansa. É essa, no fundo, a razão de existir da programação: mandar a máquina repetir uma tarefa mil, um milhão, um bilhão de vezes sem reclamar. Os laços são a ferramenta que dá acesso a esse poder. O C oferece três, e cada um brilha em uma situação. Hoje você vai aprender a escolher o certo — e a nunca deixar um laço rodar para sempre por acidente.

O laço while

O while repete um bloco enquanto uma condição for verdadeira. Ele testa antes de cada repetição:

#include <stdio.h>

int main(void) {
    int contador = 1;

    while (contador <= 5) {
        printf("Contagem: %d\n", contador);
        contador++; // sem isto, o laço nunca termina!
    }

    return 0;
}

Três ingredientes são obrigatórios em todo laço, e o esquecimento de qualquer um causa bugs: uma inicialização (contador = 1), uma condição de parada (contador <= 5) e uma atualização que aproxima o laço do fim (contador++). Esqueça o contador++ e você terá um laço infinito — o programa imprime "Contagem: 1" para sempre.

O laço do-while: pelo menos uma vez

O do-while é irmão do while, com uma diferença crucial: ele testa a condição depois de executar o bloco. Portanto, o corpo roda ao menos uma vez, aconteça o que acontecer:

#include <stdio.h>

int main(void) {
    int numero;

    do {
        printf("Digite um número positivo: ");
        scanf("%d", &numero);
    } while (numero <= 0);

    printf("Obrigado! Você digitou %d.\n", numero);
    return 0;
}

Esse é o caso de uso clássico: pedir uma entrada ao usuário e repetir enquanto ela for inválida. Faz sentido perguntar pelo menos uma vez antes de checar. Note o ponto e vírgula ao fim do while(...) — no do-while, ele é obrigatório.

O laço for: quando você sabe quantas vezes

O for reúne os três ingredientes — inicialização, condição e atualização — em uma única linha, o que o torna o laço ideal quando você conhece o número de repetições:

#include <stdio.h>

int main(void) {
    for (int i = 0; i < 5; i++) {
        printf("i = %d\n", i);
    }
    return 0;
}

A estrutura é for (inicialização; condição; atualização). Ela executa nesta ordem: inicializa i uma vez; testa a condição; se verdadeira, roda o corpo; executa a atualização; testa de novo; e assim por diante. Repare que declaramos i dentro do for — assim ele existe só durante o laço, respeitando o princípio do escopo mínimo que vimos no artigo sobre funções.

Por convenção histórica, contadores de laço em C começam em 0 e usam < (não <=). Isso não é capricho: mais tarde, ao percorrer vetores, essa contagem "de 0 a n−1" vai casar exatamente com a forma como os índices funcionam.

Qual laço usar?

A regra prática: use for quando souber o número de repetições (percorrer um vetor, contar de 1 a 100); use while quando repetir até uma condição incerta (ler um arquivo até o fim, esperar um evento); use do-while quando precisar garantir ao menos uma execução (menus, validação de entrada). Os três são intercambiáveis na teoria — qualquer um pode simular os outros —, mas escolher o certo torna a intenção do código óbvia para quem lê.

Interrompendo e pulando: break e continue

Duas palavras dão controle fino sobre o laço. O break abandona o laço imediatamente:

#include <stdio.h>

int main(void) {
    for (int i = 1; i <= 100; i++) {
        if (i * i > 50) {
            printf("O primeiro quadrado acima de 50 é %d² = %d\n", i, i * i);
            break; // achou, não precisa continuar
        }
    }
    return 0;
}

O continue pula para a próxima repetição, ignorando o resto do corpo:

#include <stdio.h>

int main(void) {
    // imprime apenas os ímpares de 1 a 10
    for (int i = 1; i <= 10; i++) {
        if (i % 2 == 0) {
            continue; // é par: pula para o próximo i
        }
        printf("%d ", i);
    }
    printf("\n");
    return 0;
}

Saída: 1 3 5 7 9

Laços aninhados

Um laço pode viver dentro de outro. É assim que se percorrem tabelas, matrizes e grades:

#include <stdio.h>

int main(void) {
    for (int linha = 1; linha <= 3; linha++) {
        for (int col = 1; col <= 3; col++) {
            printf("%d ", linha * col);
        }
        printf("\n"); // quebra de linha ao fim de cada linha
    }
    return 0;
}

Saída (uma tabuada 3×3):

1 2 3
2 4 6
3 6 9

O laço interno completa todas as suas voltas para cada volta do externo. Cuidado com o custo: dois laços de mil voltas cada resultam em um milhão de repetições. Essa noção é a semente do que, mais tarde, chamaremos de complexidade de algoritmos.

Um programa completo

#include <stdio.h>

int main(void) {
    int n = 10;
    long soma = 0;
    long fatorial = 1;

    for (int i = 1; i <= n; i++) {
        soma += i;         // acumula a soma
        fatorial *= i;     // acumula o produto
    }

    printf("Soma de 1 a %d: %ld\n", n, soma);
    printf("%d! = %ld\n", n, fatorial);
    return 0;
}

Saída:

Soma de 1 a 10: 55
10! = 3628800

Repare no padrão de acumulador: uma variável começa num valor neutro (0 para soma, 1 para produto) e é atualizada a cada volta. Esse padrão aparece em toda parte na programação — some, conte, encontre o maior, junte texto.

O que vem a seguir

Com decisões e repetições, você já tem uma linguagem Turing-completa nas mãos: em teoria, qualquer programa pode ser escrito só com o que vimos até aqui. Mas falta guardar coleções de dados. No próximo artigo, vamos aos vetores — e será nosso primeiro encontro real com a memória disposta em sequência. É também a porta de entrada para o tema mais importante e temido do C: os ponteiros.

Fontes e leituras recomendadas

Exercícios

Exercício 1

Escreva um for que imprima os números de 10 até 1, em ordem decrescente.

Ver resposta

✓ Resposta: Basta inverter a inicialização, a condição e a atualização:

for (int i = 10; i >= 1; i--) {
    printf("%d\n", i);
}

Exercício 2

Usando while, calcule e imprima a soma de todos os números pares de 1 a 100.

Ver resposta

✓ Resposta:

#include <stdio.h>
int main(void) {
    int i = 2;
    int soma = 0;
    while (i <= 100) {
        soma += i;
        i += 2; // salta de 2 em 2, só nos pares
    }
    printf("Soma dos pares: %d\n", soma); // 2550
    return 0;
}

Exercício 3

Converta este for em um while equivalente, produzindo a mesma saída: for (int i = 0; i < 5; i++) printf("%d\n", i);

Ver resposta

✓ Resposta: O while precisa reproduzir os três ingredientes separadamente:

int i = 0;            // inicialização
while (i < 5) {       // condição
    printf("%d\n", i);
    i++;              // atualização
}

Isso mostra que o for é, na prática, um while mais compacto.

Exercício 4

Escreva um laço que imprima a tabuada do 7 (de 7×1 a 7×10), uma multiplicação por linha, no formato 7 x 1 = 7.

Ver resposta

✓ Resposta:

for (int i = 1; i <= 10; i++) {
    printf("7 x %d = %d\n", i, 7 * i);
}

Exercício 5

Usando dois laços aninhados, imprima um triângulo de asteriscos com 5 linhas, onde a primeira linha tem 1 asterisco e a última tem 5.

Ver resposta

✓ Resposta: O laço interno usa o contador do externo como limite:

#include <stdio.h>
int main(void) {
    for (int linha = 1; linha <= 5; linha++) {
        for (int a = 1; a <= linha; a++) {
            printf("*");
        }
        printf("\n");
    }
    return 0;
}

Saída:

*
**
***
****
*****

Como o limite do laço interno (a <= linha) cresce a cada volta do externo, cada linha ganha um asterisco a mais.

Comentários

Mais em Linguagem C

Árvores Binárias de Busca
Árvores Binárias de Busca

Inserir 1, 2, 3, 4, 5 numa árvore de busca produz uma lista encadeada…

Compilação Separada: Cabeçalhos e Múltiplos Arquivos
Compilação Separada: Cabeçalhos e Múltiplos Arquivos

Incluir um .c em vez de ligar os objetos é o erro que rende definição múltipla…

Ponteiros para Struct e o Operador Seta
Ponteiros para Struct e o Operador Seta

Os parênteses em (*ptr).x não são preciosismo: sem eles o ponto vence o…