Nesta aula, vamos explorar duas das estruturas de repetição mais fundamentais da linguagem C: while e do-while. Ambas permitem executar um bloco de código repetidamente enquanto uma condição for verdadeira, mas diferem no momento em que a condição é avaliada. Compreender essas diferenças é essencial para escrever loops corretos e eficientes.

O loop while é conhecido como uma estrutura de pré-teste, pois a condição é verificada antes da execução do bloco. Já o do-while é uma estrutura de pós-teste, garantindo que o bloco seja executado pelo menos uma vez. Vamos detalhar cada um, com exemplos práticos e boas práticas.

Diferenças

A principal diferença entre while e do-while está no momento da avaliação da condição. No while, a condição é testada antes de cada iteração; se for falsa desde o início, o bloco nunca é executado. No do-while, a condição é testada após a execução do bloco, garantindo que o bloco seja executado ao menos uma vez.

Exemplo: se você precisa ler dados do usuário até que ele forneça um valor válido, o do-while é mais adequado, pois você precisa ler pelo menos uma vez antes de verificar. Já para percorrer um array até encontrar um elemento, o while é natural, pois pode não haver iteração se o array estiver vazio.

// Exemplo: while vs do-while
#include <stdio.h>

int main() {
    int x = 10;

    // while: condição testada antes
    while (x < 5) {
        printf("while: x = %d\n", x);
        x++;
    }
    printf("Após while, x = %d\n\n", x);

    // do-while: condição testada depois
    x = 10;
    do {
        printf("do-while: x = %d\n", x);
        x++;
    } while (x < 5);
    printf("Após do-while, x = %d\n", x);

    return 0;
}

No exemplo acima, o bloco while nunca executa porque a condição é falsa desde o início. Já o do-while executa uma vez, mesmo que a condição seja falsa, imprimindo "do-while: x = 10".

Condições

As condições em while e do-while são expressões booleanas (em C, qualquer valor diferente de zero é verdadeiro). A condição pode ser simples ou composta, envolvendo operadores relacionais, lógicos ou até mesmo chamadas de função que retornam um valor inteiro.

É importante garantir que a condição se torne falsa em algum momento, caso contrário teremos um loop infinito. Além disso, a condição pode ser alterada dentro do bloco (por exemplo, incrementando uma variável de controle) ou depender de entrada externa.

// Exemplo: condição com entrada do usuário
#include <stdio.h>

int main() {
    int numero;
    printf("Digite números (0 para sair):\n");
    do {
        printf("Número: ");
        scanf("%d", &numero);
        printf("Você digitou %d\n", numero);
    } while (numero != 0);

    printf("Fim do programa.\n");
    return 0;
}

Neste exemplo, a condição depende do valor digitado pelo usuário. O loop continua enquanto o número for diferente de zero. O uso de do-while é apropriado porque precisamos ler pelo menos um número antes de verificar.

Loops infinitos

Loops infinitos ocorrem quando a condição nunca se torna falsa. Eles podem ser intencionais (por exemplo, em servidores que rodam para sempre) ou acidentais (como esquecer de atualizar a variável de controle). Em C, um loop infinito pode ser criado com while(1) ou for(;;). O do-while também pode ser infinito se a condição for sempre verdadeira.

Para evitar loops infinitos acidentais, sempre verifique se a condição será eventualmente falsa. Use ferramentas de debug ou adicione um contador de segurança em loops potencialmente longos.

// Loop infinito intencional (exemplo de menu)
#include <stdio.h>

int main() {
    int opcao;
    while (1) {
        printf("Menu:\n");
        printf("1. Opção 1\n");
        printf("2. Opção 2\n");
        printf("3. Sair\n");
        printf("Escolha: ");
        scanf("%d", &opcao);
        if (opcao == 3) break;
        printf("Você escolheu %d\n", opcao);
    }
    printf("Saindo...\n");
    return 0;
}

Neste exemplo, o loop é infinito por design (while(1)), mas a saída é controlada pelo comando break. Sem o break, o programa nunca terminaria.

Quando usar cada um

Use while quando você deseja que a condição seja verificada antes de cada execução, ou seja, quando pode ser que o bloco nunca seja executado. Exemplos: percorrer uma lista até encontrar um elemento, ler um arquivo até o fim, ou aguardar um evento.

Use do-while quando o bloco precisar ser executado pelo menos uma vez, independentemente da condição. É ideal para menus interativos, validação de entrada (como ler um número até que esteja em um intervalo), ou qualquer situação onde a primeira iteração é obrigatória.

Em geral, prefira while para a maioria dos casos, pois é mais claro que o bloco pode não executar. O do-while é menos comum, mas muito útil em cenários específicos.

// Comparação prática: validação de idade
#include <stdio.h>

int main() {
    int idade;
    // Com while: precisamos de uma inicialização extra
    printf("Digite sua idade (0-150): ");
    scanf("%d", &idade);
    while (idade < 0 || idade > 150) {
        printf("Idade inválida. Digite novamente: ");
        scanf("%d", &idade);
    }

    // Com do-while: mais elegante
    int idade2;
    do {
        printf("Digite sua idade (0-150): ");
        scanf("%d", &idade2);
    } while (idade2 < 0 || idade2 > 150);

    printf("Idades válidas: %d e %d\n", idade, idade2);
    return 0;
}

O exemplo mostra que a validação com do-while é mais concisa, evitando a duplicação da leitura inicial.

Boas práticas e observações finais

Sempre inicialize as variáveis usadas na condição antes do loop. Evite modificar a condição dentro do bloco de maneiras inesperadas. Use nomes de variáveis descritivos para as condições. Considere usar break e continue com moderação, pois podem tornar o fluxo menos legível. Prefira loops com condição clara e evite loops infinitos acidentais.

Exercícios

  1. Escreva um programa em C que use um loop while para imprimir os números de 1 a 10.
  2. ✓ Resposta:
    #include <stdio.h>
    
    int main() {
        int i = 1;
        while (i <= 10) {
            printf("%d ", i);
            i++;
        }
        printf("\n");
        return 0;
    }
  3. Escreva um programa que use um loop do-while para ler números inteiros do usuário até que ele digite um número negativo. Ao final, exiba a soma dos números positivos digitados.
  4. ✓ Resposta:
    #include <stdio.h>
    
    int main() {
        int num, soma = 0;
        do {
            printf("Digite um número (negativo para sair): ");
            scanf("%d", &num);
            if (num >= 0) soma += num;
        } while (num >= 0);
        printf("Soma dos positivos: %d\n", soma);
        return 0;
    }
  5. Crie um loop infinito intencional que exiba um menu com três opções (1, 2, 3) e use break para sair quando o usuário digitar 3.
  6. ✓ Resposta:
    #include <stdio.h>
    
    int main() {
        int opcao;
        while (1) {
            printf("Menu:\n");
            printf("1. Opção 1\n");
            printf("2. Opção 2\n");
            printf("3. Sair\n");
            printf("Escolha: ");
            scanf("%d", &opcao);
            if (opcao == 3) break;
            printf("Você escolheu %d\n", opcao);
        }
        return 0;
    }
  7. Explique a diferença entre while e do-while com suas próprias palavras e dê um exemplo onde do-while é mais adequado.
  8. ✓ Resposta: O while testa a condição antes de executar o bloco, podendo não executá-lo nenhuma vez. O do-while testa depois, garantindo pelo menos uma execução. Exemplo adequado: validação de entrada, onde precisamos ler ao menos um valor antes de verificar se é válido.
  9. Escreva um programa que leia um número inteiro N e, usando um loop while, calcule e exiba o fatorial de N (considere N >= 0).
  10. ✓ Resposta:
    #include <stdio.h>
    
    int main() {
        int n, i = 1;
        unsigned long long fat = 1;
        printf("Digite um número inteiro não negativo: ");
        scanf("%d", &n);
        if (n < 0) {
            printf("Número inválido.\n");
            return 1;
        }
        while (i <= n) {
            fat *= i;
            i++;
        }
        printf("Fatorial de %d = %llu\n", n, fat);
        return 0;
    }

Referências