Carregando
Todos os artigos
Programação 21 min de leitura

Os Laços While e Do-While: Um Guia Visual e Prático

Aprenda os laços while e do-while de forma visual: como verificam antes ou depois, os três ingredientes de todo laço, validação de entrada, sentinelas, break e continue, armadilhas de laço infinito, exemplos reais divertidos (dobrar papel, Collatz) e exercícios.

nbkhope
Nesta página

Computadores nunca ficam entediados. Peça para um deles fazer algo um milhão de vezes e ele faz, sem reclamar. Os laços (ou loops) são o jeito de pedir. Este artigo trata dos dois laços feitos para "continue até algo acontecer": o laço while ("enquanto") e o seu irmão, o laço do-while ("faça… enquanto").

Você vai ver como eles funcionam passo a passo, dobrar uma folha de papel até a Lua, perseguir um número que pula como granizo, montar um jogo e um menu e aprender a reconhecer o laço infinito antes que ele trave o seu programa. Exercícios com respostas fecham o artigo.

Os exemplos estão em C++, mas while e do-while são quase iguais em C, Java, C#, JavaScript e muitas outras linguagens.

while vs. do-while num relance

Os dois laços repetem um bloco de código enquanto uma condição continuar verdadeira. A única diferença é quando eles a verificam:

O while verifica antes; o do-while verifica depois, em dois fluxogramas. O laço while roda 0 ou mais vezes: no início, verifica a condição; se true, roda o corpo e volta para verificar de novo; se false, vai para depois do laço. O laço do-while roda 1 ou mais vezes: no início, roda o corpo primeiro e depois verifica a condição; se true, volta para rodar o corpo de novo; se false, vai para depois do laço. Se a condição já começa falsa, o while nunca roda o corpo, mas o do-while ainda o roda uma vez
while (condicao) {
    // roda de novo e de novo, enquanto a condição for verdadeira
}
do {
    // roda uma vez primeiro, depois de novo enquanto a condição for verdadeira
} while (condicao);   // repare no ponto e vírgula!

O laço while

Aqui está o exemplo clássico, contando de 1 a 10:

1#include <iostream>
2
3int main() {
4    int contador = 1;              // 1. início
5
6    while (contador <= 10) {       // 2. verificação
7        std::cout << "O valor do contador é " << contador << "\n";
8        contador++;                // 3. passo: sem isto, o laço nunca acaba
9    }
10
11    std::cout << "Pronto! Agora o contador vale " << contador << "\n";
12}

Execução de exemplo:

1O valor do contador é 1
2O valor do contador é 2
3O valor do contador é 3
4O valor do contador é 4
5O valor do contador é 5
6O valor do contador é 6
7O valor do contador é 7
8O valor do contador é 8
9O valor do contador é 9
10O valor do contador é 10
11Pronto! Agora o contador vale 11

Passo a passo: contador começa em 1. 1 ≤ 10? Sim, então o corpo imprime e soma um. De volta à verificação: 2 ≤ 10? Sim… e assim por diante, até contador virar 11. Aí 11 ≤ 10 é falso, e o programa segue depois do laço. Por isso a última linha diz 11: o laço para justamente porque o contador passou de 10.

Os três ingredientes

Quase todo laço while tem as mesmas três partes. Deixe uma de fora, ou erre nela, e o laço se comporta mal:

Todo laço while precisa de três ingredientes. 1, início: int contador = 1; dá um valor inicial à variável do laço. Esqueça e o laço começa com lixo. 2, verificação: while (contador <= 10) decide se dá mais uma volta. Erre e você ganha um passo a mais ou a menos. 3, passo: contador++; anda em direção à linha de chegada. Esqueça e o laço nunca acaba. Um laço for junta as mesmas três partes numa linha só: for (int contador = 1; contador <= 10; contador++)

Quando você não sabe quantas vezes

Dobre uma folha de papel até a Lua

Uma folha de papel tem cerca de 0,1 mm de espessura. Cada vez que você a dobra ao meio, a espessura dobra. Quantas dobras até ela alcançar a Lua, a 384.400 km de distância? Difícil fazer essa conta de cabeça, então deixe um laço while descobrir:

1#include <iostream>
2
3int main() {
4    double espessuraEmMetros = 0.0001;  // uma folha de papel: 0,1 mm
5    const double distanciaAteALua = 384400000.0;  // 384.400 km
6    int dobras = 0;
7
8    while (espessuraEmMetros < distanciaAteALua) {
9        espessuraEmMetros *= 2;  // cada dobra dobra a espessura
10        dobras++;
11    }
12
13    std::cout << "Dobras para chegar à Lua: " << dobras << "\n";
14    std::cout << "Espessura final: " << espessuraEmMetros / 1000 << " km\n";
15}

Execução de exemplo:

Dobras para chegar à Lua: 42
Espessura final: 439805 km

Só 42! Aqui está a viagem inteira, dobra por dobra:

Dobre uma folha de papel de 0,1 mm ao meio, de novo e de novo. Cada dobra dobra a espessura, mostrada numa escala logarítmica em que cada linha da grade vale 1.000 vezes a de baixo. 7 dobras: 1,3 cm, até onde a maioria consegue à mão. 23 dobras: 839 m, mais alto que o Burj Khalifa. 27 dobras: 13 km, mais alto que o Everest. 32 dobras: 429 km, além da Estação Espacial. 42 dobras: 439.805 km, além da Lua. Dobrar cresce de um jeito assustador. O laço roda só 42 vezes, mas seria difícil adivinhar esse número antes de rodá-lo

(Na vida real, ninguém dobrou uma folha de papel mais do que uma dúzia de vezes, mais ou menos. Mas a matemática, e o laço, não ligam para isso.)

Os números de granizo

Aqui vai um quebra-cabeça famoso. Escolha um número inteiro qualquer. Se for par, divida por 2. Se for ímpar, triplique e some 1. Repita até chegar ao 1. Quantos passos isso leva?

1#include <iostream>
2
3int main() {
4    long long numero = 27;
5    int passos = 0;
6    long long maior = numero;
7
8    while (numero != 1) {
9        if (numero % 2 == 0) {
10            numero = numero / 2;
11        } else {
12            numero = 3 * numero + 1;
13        }
14        passos++;
15        if (numero > maior) maior = numero;
16    }
17
18    std::cout << "O 27 chega ao 1 depois de " << passos << " passos\n";
19    std::cout << "Maior valor no caminho: " << maior << "\n";
20}

Execução de exemplo:

O 27 chega ao 1 depois de 111 passos
Maior valor no caminho: 9232

Rodamos o mesmo laço para todos os números iniciais de 1 a 30:

Quantos passos até chegar ao 1, para valores iniciais de 1 a 30. Regra: se for par, divida por 2; se for ímpar, triplique e some 1. A maioria dos números precisa de 0 a 23 passos, mas o 27 leva 111 passos. Vizinhos se comportam de forma completamente diferente: o 26 precisa de 10 passos e o 27 de 111. Não dá para saber a quantidade antes, e é exatamente aí que se usa um laço while

E aqui está a viagem maluca que o 27 faz até descer:

A viagem maluca do 27: sobe até 9.232 e desce até 1. O valor sobe e desce por 111 passos, subindo devagar no começo, disparando até um pico de 9.232 no passo 77 e depois caindo rápido até o 1 no passo 111. Os matemáticos chamam esses números de granizo, porque sobem e descem como o granizo numa nuvem de tempestade. Ninguém provou que todo número inicial chega ao 1; é um problema em aberto chamado conjectura de Collatz

Desmontando um número, dígito por dígito

% 10 dá o último dígito de um número, e / 10 o arranca fora. Repita até não sobrar nada:

1#include <iostream>
2
3int main() {
4    int numero = 90210;
5    int quantidadeDeDigitos = 0;
6    int somaDosDigitos = 0;
7
8    while (numero > 0) {
9        somaDosDigitos += numero % 10;  // o último dígito
10        numero /= 10;                   // arranca o último dígito
11        quantidadeDeDigitos++;
12    }
13
14    std::cout << "Dígitos: " << quantidadeDeDigitos << ", soma dos dígitos: " << somaDosDigitos << "\n";
15}
Dígitos: 5, soma dos dígitos: 12

Lendo até um sinal de parada

Uma sentinela é um valor especial que quer dizer "terminei". Este programa lê notas até o usuário digitar -1:

1#include <iostream>
2
3int main() {
4    int total = 0;
5    int quantidade = 0;
6    int nota;
7
8    std::cout << "Digite as notas (-1 para terminar): ";
9    std::cin >> nota;
10    while (nota != -1) {
11        total += nota;
12        quantidade++;
13        std::cin >> nota;
14    }
15
16    if (quantidade > 0) {
17        std::cout << quantidade << " notas, média " << static_cast<double>(total) / quantidade << "\n";
18    } else {
19        std::cout << "Nenhuma nota digitada\n";
20    }
21}

Execução de exemplo:

Digite as notas (-1 para terminar): 90 85 77 -1
3 notas, média 84

Se o primeiro valor já for -1, o corpo do laço nunca roda, e o programa diz "Nenhuma nota digitada". Esse comportamento de "zero vezes está tudo bem" é exatamente o propósito do laço while.

O laço do-while

Um do-while roda o corpo primeiro e verifica a condição depois. Então o corpo sempre roda pelo menos uma vez. Aqui está a diferença num programa só, com uma condição que já começa falsa:

1#include <iostream>
2
3int main() {
4    int numero = 2;
5
6    do {
7        std::cout << "o do-while vê " << numero << "\n";
8        numero++;
9    } while (numero < 2);
10
11    numero = 2;
12    while (numero < 2) {
13        std::cout << "o while vê " << numero << "\n";
14        numero++;
15    }
16
17    std::cout << "Fim\n";
18}
o do-while vê 2
Fim

O do-while imprimiu uma vez antes de descobrir que 3 < 2 é falso. O laço while verificou 2 < 2 primeiro, achou falso e nunca rodou o corpo.

Perfeito para: perguntar até a resposta ser válida

Não dá para conferir uma resposta antes de fazer a pergunta, e é por isso que validar entrada é o trabalho clássico do do-while:

1#include <iostream>
2
3int main() {
4    int avaliacao;
5
6    do {
7        std::cout << "Dê uma nota de 1 a 5 ao filme: ";
8        std::cin >> avaliacao;
9        if (avaliacao < 1 || avaliacao > 5) {
10            std::cout << "  Isso não está entre 1 e 5, tente de novo.\n";
11        }
12    } while (avaliacao < 1 || avaliacao > 5);
13
14    std::cout << "Obrigado! Você deu " << avaliacao << " estrelas.\n";
15}

Execução de exemplo:

1Dê uma nota de 1 a 5 ao filme: 7
2  Isso não está entre 1 e 5, tente de novo.
3Dê uma nota de 1 a 5 ao filme: 0
4  Isso não está entre 1 e 5, tente de novo.
5Dê uma nota de 1 a 5 ao filme: 4
6Obrigado! Você deu 4 estrelas.

Perfeito para: menus

Um menu precisa aparecer pelo menos uma vez e continuar voltando até o usuário escolher "sair":

1#include <iostream>
2
3int main() {
4    int opcao;
5    int cafes = 0;
6
7    do {
8        std::cout << "1) Pedir café  2) Ver total  0) Sair\n> ";
9        std::cin >> opcao;
10        if (opcao == 1) {
11            cafes++;
12            std::cout << "Café pedido!\n";
13        } else if (opcao == 2) {
14            std::cout << "Você pediu " << cafes << " café(s).\n";
15        }
16    } while (opcao != 0);
17
18    std::cout << "Tchau!\n";
19}

Execução de exemplo:

11) Pedir café  2) Ver total  0) Sair
2> 1
3Café pedido!
41) Pedir café  2) Ver total  0) Sair
5> 1
6Café pedido!
71) Pedir café  2) Ver total  0) Sair
8> 2
9Você pediu 2 café(s).
101) Pedir café  2) Ver total  0) Sair
11> 0
12Tchau!

Perfeito para: jogos

Um jogo de adivinhação: continue perguntando até o palpite estar certo. Você precisa de pelo menos um palpite, então o do-while se encaixa naturalmente:

1#include <iostream>
2
3int main() {
4    const int segredo = 37;
5    int palpite;
6    int tentativas = 0;
7
8    do {
9        std::cout << "Palpite (1-100): ";
10        std::cin >> palpite;
11        tentativas++;
12        if (palpite < segredo) {
13            std::cout << "  Mais alto!\n";
14        } else if (palpite > segredo) {
15            std::cout << "  Mais baixo!\n";
16        }
17    } while (palpite != segredo);
18
19    std::cout << "Acertou em " << tentativas << " tentativas!\n";
20}

Execução de exemplo:

1Palpite (1-100): 50
2  Mais baixo!
3Palpite (1-100): 25
4  Mais alto!
5Palpite (1-100): 37
6Acertou em 3 tentativas!

O jeito de escolher cada palpite faz uma diferença enorme. Pusemos um programa para jogar com todos os números secretos de 1 a 100 usando duas estratégias: contar 1, 2, 3…, ou sempre chutar o meio do que sobrou:

Adivinhe um número de 1 a 100: duas estratégias, cada uma testada com todos os números secretos por um programa em C++. Contar 1, 2, 3, 4: 50,5 palpites em média, 100 no pior caso. Dividir ao meio, 50, depois 25 ou 75: 5,8 palpites em média, 7 no pior caso. Os dois são laços do-while que rodam até o palpite estar certo. Uma regra mais esperta para escolher o próximo palpite corta o trabalho de uns 50 palpites para menos de 6

O mesmo laço, uma regra mais esperta dentro dele, e 7 palpites sempre bastam. Essa ideia de dividir ao meio se chama busca binária e aparece em todo canto da computação.

Pilotando o laço: break e continue

Duas palavras-chave deixam você mudar o rumo de dentro do corpo:

  • break sai do laço na hora.
  • continue pula o resto desta volta e vai direto para a verificação.
break sai do laço; continue pula para a próxima volta. Num laço while (true) que soma um ao numero, se numero % 3 == 0, o continue volta para o topo do laço, pulando o resto da volta. Se numero > 10, o break sai do laço na hora e segue depois dele. Senão, imprime o numero. Saída: 1 2 4 5 7 8 10
1#include <iostream>
2
3int main() {
4    int numero = 0;
5    while (true) {
6        numero++;
7        if (numero % 3 == 0) {
8            continue;  // pula os múltiplos de 3 e volta para a verificação
9        }
10        if (numero > 10) {
11            break;     // sai do laço de vez
12        }
13        std::cout << numero << " ";
14    }
15    std::cout << "\nParou no " << numero << "\n";
16}
1 2 4 5 7 8 10 
Parou no 11

while (true) parece rodar para sempre, e rodaria, sem o break. É um padrão comum e prático quando o lugar natural de parar fica no meio do corpo.

Laços infinitos: o laço que nunca acaba

O bug de laço mais comum é um laço que nunca para. Aqui estão três jeitos clássicos de escrever um:

Três jeitos de escrever um laço que nunca acaba. Esqueceu o passo: contador começa em 1 e, enquanto contador <= 10, é impresso, mas nunca muda, então fica 1 para sempre. Passo na direção errada: contador começa em 10, enquanto contador > 0 é impresso e faz contador++, então se afasta do 0. Esperando um decimal exato: valor começa em 0.0 e, enquanto valor != 1.0, soma 0.1, mas o valor pula de 0.99999999999999989 para 1.0999999999999999. Dica: se um programa parece travado, aperte Ctrl+C no terminal para pará-lo

O terceiro surpreende quase todo mundo, então vamos vê-lo acontecer (com uma rede de proteção para parar depois de 12 voltas):

1#include <iostream>
2
3int main() {
4    double valor = 0.0;
5    int passos = 0;
6
7    std::cout.precision(17);
8    while (valor != 1.0 && passos < 12) {   // passos < 12 é uma rede de proteção
9        valor += 0.1;
10        passos++;
11        std::cout << "passo " << passos << ": " << valor << "\n";
12    }
13}
1passo 1: 0.10000000000000001
2passo 2: 0.20000000000000001
3passo 3: 0.30000000000000004
4passo 4: 0.40000000000000002
5passo 5: 0.5
6passo 6: 0.59999999999999998
7passo 7: 0.69999999999999996
8passo 8: 0.79999999999999993
9passo 9: 0.89999999999999991
10passo 10: 0.99999999999999989
11passo 11: 1.0999999999999999
12passo 12: 1.2

Os computadores guardam decimais em binário, e 0,1 não pode ser guardado de forma exata, assim como 1/3 não pode ser escrito exatamente como 0,333… em decimal. Os errinhos se acumulam, e o valor nunca cai exatamente em 1.0. A solução é nunca esperar por um decimal exato: compare com < em vez de !=, ou conte passos inteiros com um int.

Que laço usar?

Que laço você deve usar? Primeira pergunta: você sabe quantas vezes vai repetir antes de o laço começar? Se sim, use for: contar até 10, visitar cada elemento de um array. Se não, segunda pergunta: o corpo precisa rodar pelo menos uma vez, como ao pedir dados? Se sim, use do-while: menus, validar entrada, jogar de novo. Se não, use while: repetir até algo acontecer, como uma soma ou um palpite
whiledo-whilefor
Verifica a condiçãoAntes de cada voltaDepois de cada voltaAntes de cada volta
Mínimo de execuções010
Melhor para"Até algo acontecer"Menus, validar entrada, "jogar de novo?"Contar, percorrer arrays
Exemplo clássicoLer até uma sentinela"Digite de 1 a 5"for (int i = 0; i < 10; i++)

A mesma ideia em outras linguagens

JavaScript tem os dois laços, igualzinho ao C++:

1let contador = 1;
2while (contador <= 3) {
3  console.log("contador vale " + contador);
4  contador++;
5}

Python tem o laço while, mas não tem do-while. O substituto de costume é um while True com break:

1while True:
2    resposta = input("Digite sim para continuar: ")
3    if resposta == "sim":
4        break

Hora de praticar

Tente fazer estes antes de espiar as respostas. Copie cada programa num arquivo, compile com g++ -std=c++20 -Wall programa.cpp e rode.

Exercício 1: Adivinhe a saída (aquecimento). O que isto imprime? Preste atenção no último número.

1int numero = 5;
2while (numero > 0) {
3    std::cout << numero << " ";
4    numero -= 2;
5}
6std::cout << "| " << numero << "\n";

Exercício 2: Ache o erro. Esta contagem regressiva nunca termina. Por quê?

1int segundos = 10;
2while (segundos > 0) {
3    std::cout << segundos << "... ";
4    segundos++;
5}
6std::cout << "Decolar!\n";

Exercício 3: Inverta um número. Usando % 10 e / 10, transforme 1234 em 4321.

Exercício 4: Três tentativas. Peça uma senha com um laço do-while. Pare quando ela estiver certa (4321) ou depois de 3 tentativas erradas, e então imprima Desbloqueado! ou Tentativas demais. Cartão bloqueado.

Exercício 5: Máximo divisor comum (desafio). Há mais de 2.000 anos, Euclides descobriu um laço que acha o maior número que divide dois números sem deixar resto: enquanto o segundo número não for 0, troque o par (primeiro, segundo) por (segundo, primeiro % segundo). Ache o máximo divisor comum de 1071 e 462, imprimindo cada passo.

Quiz rápido

  1. A condição de um laço while é falsa já na primeira verificação. Quantas vezes o corpo roda? E num do-while?
  2. O que está faltando em int contador = 0; while (contador < 5) { std::cout << contador; }?
  3. Qual é a diferença entre break e continue?
  4. Por que while (valor != 1.0) é perigoso quando valor sobe de 0.1 em 0.1?
  5. Que laço combina com "mostrar o menu até o usuário escolher Sair"?

Respostas

Exercício 1: 5 3 1 | -1. O laço imprime 5, 3 e 1. Depois do 1, ele subtrai 2 e chega a -1, a verificação -1 > 0 falha e o laço para. A variável fica com o último valor, -1.

5 3 1 | -1

Exercício 2: segundos++ se afasta da linha de chegada: 10, 11, 12… e segundos > 0 continua verdadeiro para sempre. O certo é segundos--:

1#include <iostream>
2
3int main() {
4    int segundos = 10;
5    while (segundos > 0) {
6        std::cout << segundos << "... ";
7        segundos--;
8    }
9    std::cout << "Decolar!\n";
10}
10... 9... 8... 7... 6... 5... 4... 3... 2... 1... Decolar!

Exercício 3: A cada volta, empurre o resultado um dígito para a esquerda (* 10) e some o último dígito do número.

1#include <iostream>
2
3int main() {
4    int numero = 1234;
5    int invertido = 0;
6
7    while (numero > 0) {
8        invertido = invertido * 10 + numero % 10;
9        numero /= 10;
10    }
11
12    std::cout << "Invertido: " << invertido << "\n";
13}
Invertido: 4321

Exercício 4: A condição junta os dois motivos para continuar perguntando: a senha está errada e ainda há tentativas.

1#include <iostream>
2
3int main() {
4    const int senhaCorreta = 4321;
5    int senha;
6    int tentativas = 0;
7
8    do {
9        std::cout << "Digite a senha: ";
10        std::cin >> senha;
11        tentativas++;
12    } while (senha != senhaCorreta && tentativas < 3);
13
14    if (senha == senhaCorreta) {
15        std::cout << "Desbloqueado!\n";
16    } else {
17        std::cout << "Tentativas demais. Cartão bloqueado.\n";
18    }
19}

Duas execuções de exemplo:

Digite a senha: 1111
Digite a senha: 4321
Desbloqueado!
1Digite a senha: 1
2Digite a senha: 2
3Digite a senha: 3
4Tentativas demais. Cartão bloqueado.

Exercício 5: Três voltas bastam:

1#include <iostream>
2
3int main() {
4    int primeiro = 1071;
5    int segundo = 462;
6
7    while (segundo != 0) {
8        int resto = primeiro % segundo;
9        std::cout << primeiro << " % " << segundo << " = " << resto << "\n";
10        primeiro = segundo;
11        segundo = resto;
12    }
13
14    std::cout << "Máximo divisor comum: " << primeiro << "\n";
15}
11071 % 462 = 147
2462 % 147 = 21
3147 % 21 = 0
4Máximo divisor comum: 21

Quiz: (1) Zero vezes no while; uma vez no do-while. (2) O passo: contador nunca muda, então o laço nunca acaba. Acrescente contador++;. (3) break sai do laço de vez; continue pula o resto da volta atual e vai para a verificação. (4) 0,1 não pode ser guardado de forma exata, então valor pula de 0.99999999999999989 para 1.0999999999999999 e nunca é exatamente 1.0. (5) do-while, porque o menu precisa aparecer pelo menos uma vez.

Cola rápida

Eu quero…Escreva
Repetir enquanto algo for verdadeirowhile (condicao) { ... }
Rodar pelo menos uma vez e depois repetirdo { ... } while (condicao);
Contar de 1 a 10int contador = 1; while (contador <= 10) { ...; contador++; }
Ler até um valor de paradastd::cin >> valor; while (valor != -1) { ...; std::cin >> valor; }
Perguntar até a resposta ser válidado { pergunta; } while (resposta inválida);
Sair do laço mais cedobreak;
Pular para a próxima voltacontinue;
Repetir até um breakwhile (true) { ...; if (terminou) break; }
Desmontar um númerowhile (numero > 0) { digito = numero % 10; numero /= 10; }
Parar um programa travadoCtrl+C no terminal

Resumindo

Os laços while são o jeito de um programa dizer "continue até…":

  • while verifica antes e pode rodar zero vezes; do-while verifica depois e sempre roda pelo menos uma vez.
  • Todo laço precisa de um início, uma verificação e um passo que caminhe até a linha de chegada.
  • Use while quando não souber a quantidade de antemão: dobrar até a Lua levou 42 voltas, e o 27 precisou de 111 passos para chegar ao 1.
  • Use do-while em menus, validação de entrada e jogos.
  • break sai de um laço; continue pula para a próxima volta.
  • Laços infinitos nascem de um passo esquecido, um passo na direção errada ou da espera por um decimal exato.

Conheça o especialista em contagem em O Laço For; para colocar decisões dentro dos seus laços, leia O Comando If; e para percorrer dados de verdade, Arrays em C++.

Obrigado pela leitura!

Conhece alguém que vai gostar? Mande para essa pessoa.

Mais artigos

Leia a seguir

Comentários

Seja gentil e específico. Comentários com links de contas novas aparecem depois da análise de um moderador.