C++ while
Até agora, cada linha dos seus programas rodou uma vez só, de cima para baixo. Mas boa parte do trabalho de um computador é repetir: mostrar os 30 alunos da turma, somar os preços de todos os produtos, perguntar de novo até a pessoa digitar algo válido. Para isso existem os laços (em inglês, loops). O mais simples deles é o while, que quer dizer "enquanto": enquanto a condição for verdadeira, repita.
- O que é um laço e como escrever um
while. - Como contar de 1 até 5, e de 5 até 1, com uma variável contadora.
- Como acumular um resultado (uma soma) ao longo das voltas.
- Como repetir uma pergunta até o usuário digitar
0. - O que é um laço infinito, como pará-lo e como evitá-lo.
- Os avisos do compilador que pegam os erros mais comuns de
while.
Todos os programas desta aula foram compilados com o g++ 15 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cpp, com o comando padrão do guia: g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa (veja Compilar com g++). Você pode rodar cada um no editor do guia.
A ideia: "enquanto tiver louça, lave um prato"
Imagine que alguém deixou um bilhete para você: "Enquanto tiver louça na pia, lave um prato." Você não sabe quantos pratos há. Mesmo assim, a instrução funciona:
- Você olha a pia: tem louça? Sim → lava um prato.
- Olha de novo: tem louça? Sim → lava mais um.
- … e assim por diante …
- Olha de novo: tem louça? Não → acabou, você vai fazer outra coisa.
Repare em três detalhes, porque eles valem para todo while:
- A pergunta vem antes de cada prato. Se a pia já estiver vazia, você não lava nenhum.
- Cada volta muda alguma coisa (um prato a menos). Sem isso, a pergunta teria sempre a mesma resposta.
- Quando a resposta vira não, você sai e continua com o resto do dia.
Em C++, a "pergunta" é uma condição, igual à do if: uma expressão que vale true (verdadeiro) ou false (falso). A diferença é que o if executa o bloco uma vez se a condição for verdadeira; o while executa de novo e de novo, enquanto ela continuar verdadeira.
1. O primeiro while: contar de 1 a 5
#include <iostream>
int main() {
int i = 1;
while (i <= 5) {
std::cout << "Volta " << i << '\n';
i++;
}
std::cout << "Fim! i agora vale " << i << '\n';
}
- int i = 1;
- A variável contadora, criada antes do laço, com o valor inicial. O nome
i(de "índice") é uma tradição para contadores; poderia servoltaoucontador. - while
- A palavra-chave do laço: "enquanto".
- (i <= 5)
- A condição, sempre entre parênteses. É testada antes de cada volta. Lê-se: "enquanto
ifor menor ou igual a 5" (os operadores estão em Comparação). - { … }
- O corpo do laço: as linhas que se repetem. Cada execução do corpo se chama uma volta (ou iteração).
- i++;
- Soma 1 ao contador (veja ++ e --). É esta linha que faz o laço andar: sem ela,
iseria 1 para sempre.
$ g++ -std=c++23 -Wall -Wextra contar.cpp -o contar $ ./contar Volta 1 Volta 2 Volta 3 Volta 4 Volta 5 Fim! i agora vale 6
Passo a passo: o que o computador fez
Siga a variável i volta por volta. Este exercício de "ser o computador" é a melhor forma de entender laços:
| Momento | Valor de i | i <= 5? | O que acontece |
|---|---|---|---|
| Antes do laço | 1 | verdadeiro | Entra: mostra Volta 1, e i vira 2 |
| 2ª pergunta | 2 | verdadeiro | Mostra Volta 2, i vira 3 |
| 3ª pergunta | 3 | verdadeiro | Mostra Volta 3, i vira 4 |
| 4ª pergunta | 4 | verdadeiro | Mostra Volta 4, i vira 5 |
| 5ª pergunta | 5 | verdadeiro | Mostra Volta 5, i vira 6 |
| 6ª pergunta | 6 | falso | Sai do laço e vai para a linha depois da } |
Por isso a última linha mostra i agora vale 6: o laço só termina quando a condição fica falsa, e ela só fica falsa quando i passa de 5. A condição foi testada 6 vezes para o corpo rodar 5 vezes.
1) início: int i = 1; · 2) condição: i <= 5 · 3) passo: i++. Se faltar qualquer uma, o laço dá errado. Guarde esse trio: na aula for, ele aparece todo numa linha só.
Missão: faça o programa contar até 10 em vez de 5. Depois experimente começar do 3 (troque o valor inicial) e veja quantas voltas sobram.
#include <iostream>
int main() {
int i = 1;
while (i <= 5) {
std::cout << "Volta " << i << '\n';
i++;
}
std::cout << "Fim! i agora vale " << i << '\n';
}
#include <iostream>
int main() {
int i = 1;
while (i <= 10) {
std::cout << "Volta " << i << '\n';
i++;
}
std::cout << "Fim! i agora vale " << i << '\n';
}
2. Contar para trás
O passo não precisa ser ++. Para uma contagem regressiva, comece do maior número, use -- e troque a condição para "maior que zero":
#include <iostream>
int main() {
int segundos = 5;
while (segundos > 0) {
std::cout << segundos << "...\n";
segundos--;
}
std::cout << "Já!\n";
}
$ g++ -std=c++23 -Wall -Wextra regressiva.cpp -o regressiva $ ./regressiva 5... 4... 3... 2... 1... Já!
Quando segundos chega a 0, a pergunta 0 > 0 é falsa e o laço termina. O Já! fica fora das chaves, então aparece uma vez só, no fim.
O passo também pode ser de 2 em 2 (i += 2), de 10 em 10, ou até uma multiplicação (valor *= 2), como você vai ver na missão final. Os atalhos += e *= estão em Operadores de Atribuição.
3. Acumular: somar de 1 até 100
Um uso clássico de laço é ir juntando um resultado. A técnica tem duas variáveis com papéis diferentes:
- o contador (
n) diz em que volta estamos; - o acumulador (
soma) guarda o total até agora. Ele começa em 0 e, a cada volta, recebe mais um pouco.
#include <iostream>
int main() {
int soma = 0;
int n = 1;
while (n <= 100) {
soma += n;
n++;
}
std::cout << "1 + 2 + ... + 100 = " << soma << '\n';
}
$ g++ -std=c++23 -Wall -Wextra soma100.cpp -o soma100 $ ./soma100 1 + 2 + ... + 100 = 5050
Nas primeiras voltas: soma vale 0, depois 0+1 = 1, depois 1+2 = 3, depois 3+3 = 6, depois 6+4 = 10… até chegar a 5050. Fazer isso à mão levaria um bom tempo; o computador faz as 100 voltas num piscar de olhos.
Se a linha int soma = 0; ficar dentro das chaves, ela roda em toda volta e zera o total. Veja esse erro na seção Quando dá errado.
Quando você não sabe quantas voltas serão
O while brilha quando o número de voltas não é conhecido de antemão. A Ana guardou R$ 100 numa aplicação que rende 10% ao mês. Em quantos meses o dinheiro dobra?
#include <iostream>
int main() {
double saldo = 100.0;
int meses = 0;
while (saldo < 200.0) {
saldo = saldo * 1.10;
meses++;
}
std::cout << "Dobrou em " << meses << " meses: R$ " << saldo << '\n';
}
$ g++ -std=c++23 -Wall -Wextra poupanca.cpp -o poupanca $ ./poupanca Dobrou em 8 meses: R$ 214.359
Aqui a condição não olha um contador, e sim o próprio saldo. A variável meses só conta as voltas para mostrar no fim. (Para mostrar 214,36 com duas casas, veja iomanip ou std::format.)
4. Repetir até o usuário digitar 0
Agora o programa vai conversar com a pessoa. A Ana quer somar os preços das compras: ela digita um preço por vez e, quando terminar, digita 0. Ninguém sabe quantos preços serão: é trabalho para o while.
#include <iostream>
int main() {
int numero = 0;
int soma = 0;
std::cout << "Digite um número (0 para parar): ";
std::cin >> numero;
while (numero != 0) {
soma += numero;
std::cout << "Digite um número (0 para parar): ";
std::cin >> numero;
}
std::cout << "Soma: " << soma << '\n';
}
- std::cin >> numero;
- A primeira leitura, antes do laço. Sem ela, o
whilenão teria um número para testar (veja Entrada com cin). - while (numero != 0)
- "Enquanto o número for diferente de zero." O
0funciona como uma sentinela: um valor combinado que significa "acabou". - soma += numero;
- Junta o número ao acumulador.
- std::cin >> numero;
- A leitura seguinte, no fim do corpo. É ela que muda a variável da condição. Depois dela, o laço volta para a pergunta
numero != 0.
$ g++ -std=c++23 -Wall -Wextra soma.cpp -o soma $ ./soma Digite um número (0 para parar): 10 Digite um número (0 para parar): 25 Digite um número (0 para parar): 7 Digite um número (0 para parar): 0 Soma: 42
O 0 não entra na soma: assim que ele é lido, a condição fica falsa e o laço termina antes do soma += numero. E se a primeira coisa digitada for 0, o corpo nem roda: a resposta é Soma: 0.
Repare que a pergunta e o cin aparecem duas vezes (antes do laço e no fim do corpo). Isso funciona, mas é repetição. Há duas formas de escrever só uma vez, que você vai ver nas próximas aulas: o do-while (pergunta primeiro, testa depois) e o while (true) com break (break e continue).
Nas caixas com entrada pronta, cada número está numa linha própria, como se alguém digitasse um número e apertasse Enter de cada vez: o editor mostra cada linha logo depois da pergunta que a leu, igual ao terminal acima. (Se a entrada fosse uma linha só, 10 25 7 0, o std::cin já teria os próximos números guardados e as perguntas seguintes sairiam grudadas, sem esperar — no terminal de verdade também.)
Missão: a entrada já traz 10 25 7 0. Crie uma segunda variável, quantos, que conta quantos números foram somados (sem contar o 0), e mostre no fim Você digitou 3 números. antes da soma.
#include <iostream>
int main() {
int numero = 0;
int soma = 0;
std::cin >> numero;
while (numero != 0) {
soma += numero;
std::cin >> numero;
}
std::cout << "Soma: " << soma << '\n';
}
#include <iostream>
int main() {
int numero = 0;
int soma = 0;
int quantos = 0;
std::cin >> numero;
while (numero != 0) {
soma += numero;
quantos++;
std::cin >> numero;
}
std::cout << "Você digitou " << quantos << " números.\n";
std::cout << "Soma: " << soma << '\n';
}
5. Zero voltas: quando a condição já começa falsa
O while testa antes de entrar. Se a condição for falsa logo na primeira pergunta, o corpo não roda nenhuma vez, e o programa segue para a linha depois da }:
#include <iostream>
int main() {
int estoque = 0;
while (estoque > 0) {
std::cout << "Vendendo uma unidade...\n";
estoque--;
}
std::cout << "Acabou o estoque.\n";
}
$ g++ -std=c++23 -Wall -Wextra nunca.cpp -o nunca $ ./nunca Acabou o estoque.
Isso não é erro: é exatamente o que você quer numa lojinha sem estoque. Quando precisar que o corpo rode pelo menos uma vez, a ferramenta é o do-while.
6. O laço infinito
Se a condição nunca ficar falsa, o laço nunca termina. Isso se chama laço infinito, e o motivo mais comum é esquecer o passo. Neste programa, falta o i++, então i vale 1 para sempre e 1 <= 5 é sempre verdadeiro:
#include <iostream>
int main() {
int i = 1;
long long voltas = 0;
std::cout << "Somando..." << std::endl;
while (i <= 5) {
voltas++;
}
std::cout << "Fim\n";
}
O compilador não reclama: um laço infinito é um programa válido (às vezes ele é até de propósito, como num jogo que roda até o jogador sair). O problema só aparece ao rodar: o programa mostra Somando... e fica parado, com o processador trabalhando a 100%, sem nunca chegar ao Fim.
Como parar: Ctrl+C
No terminal, aperte Ctrl+C. O terminal manda um aviso de "interromper" ao programa, que é encerrado na hora. O echo $? mostra o código de saída do último programa; 130 quer dizer "interrompido por Ctrl+C":
$ ./infinito2 Somando... ^C « aqui a Ana apertou Ctrl+C $ echo $? 130
Como testar sem medo: timeout
Quando você suspeita que um programa pode travar, rode-o com o comando timeout, que dá um prazo em segundos e encerra o programa se ele passar disso. É assim que os laços infinitos deste guia foram testados:
$ timeout 3 ./infinito2 Somando... « e 3 segundos de espera $ echo $? 124
O código 124 é o jeito do timeout dizer "o prazo acabou e eu tive que encerrar o programa". Se o programa tivesse terminado sozinho a tempo, o código seria o dele (normalmente 0).
std::endl aqui?
O std::endl pula a linha e manda o texto para a tela na hora (veja Novas Linhas). Com '\n', o texto pode ficar guardado esperando, e um programa encerrado à força nem sempre chega a mostrá-lo. Num programa que pode travar, é bom que a última mensagem apareça.
Um laço infinito no editor deixa a aba ocupada até você parar. Antes de clicar em Rodar, confira as três peças: início, condição e passo.
Como evitar
Antes de rodar um while, faça a si mesmo três perguntas:
- O que muda a cada volta? Alguma linha do corpo precisa mudar uma variável que aparece na condição (
i++,segundos--, um novostd::cin >> numero). - A mudança vai na direção certa? Com
while (i <= 5), oiprecisa subir. Umi--por engano fariaidescer para sempre. - O valor de parada é alcançável? Com
while (i != 10)ei += 3começando do 1, oipassa por 1, 4, 7, 10: para. Começando do 0, passa por 0, 3, 6, 9, 12… e pula o 10. Prefira<e<=a!=em contadores.
Há também laços infinitos de propósito: while (true) { … }. Eles têm uma saída escondida no meio do corpo, o break. Isso tem aula própria: break e continue.
Formas diferentes de escrever
Chaves num laço de uma linha
Como no if, se o corpo tiver uma única instrução, as chaves podem ser omitidas:
while (segundos > 0)
std::cout << segundos-- << "...\n";
Funciona, mas é fácil acrescentar uma segunda linha achando que ela está dentro do laço. Use sempre as chaves: é o que este guia faz.
Onde fazer o passo
i++ numa linha separada (como nos exemplos) é a forma mais clara. Também existe std::cout << i++;, que mostra e soma ao mesmo tempo. Para quem está começando, uma coisa por linha.
Qual laço usar?
| Situação | Laço recomendado |
|---|---|
| Não sei quantas voltas serão (até o usuário digitar 0, até o saldo dobrar) | while |
| O corpo precisa rodar pelo menos uma vez (menu, "deseja continuar?") | do-while |
| Sei quantas voltas serão (de 1 a 10, cada aluno da turma) | for |
| Passar por todos os itens de uma lista | for de intervalo |
Quando dá errado
$ timeout 3 ./infinito2 Somando... $ echo $? 124
O que aconteceu: nada no corpo muda a variável da condição, então ela continua verdadeira para sempre. O timeout encerrou o programa depois de 3 segundos (código 124).
Como consertar: acrescente o passo (i++;) dentro das chaves. Para sair de um programa travado no terminal, Ctrl+C.
while ( … )
#include <iostream>
int main() {
int i = 1;
while (i <= 5); {
std::cout << i << '\n';
i++;
}
}
$ g++ -std=c++23 -Wall -Wextra pontovirgula.cpp -o pontovirgula
pontovirgula.cpp: In function ‘int main()’:
pontovirgula.cpp:5:5: warning: this ‘while’ clause does not guard... [-Wmisleading-indentation]
5 | while (i <= 5); {
| ^~~~~
pontovirgula.cpp:5:21: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘while’
5 | while (i <= 5); {
| ^
$ timeout 3 ./pontovirgula
$ echo $?
124
O que significa: "esta cláusula while não protege… esta instrução, mas a indentação engana, como se ela estivesse protegida pelo while". O ; logo depois do parêntese é um corpo vazio: o laço é só while (i <= 5);, que repete "nada" para sempre, porque o i++ ficou fora dele. As chaves de baixo viraram um bloco comum, que nunca é alcançado. O programa nem mostra o 1.
No editor do guia, o Clang avisa com outras palavras: warning: while loop has empty body [-Wempty-body] ("o laço while tem corpo vazio").
Como consertar: apague o ; entre o ) e a {. Um while nunca leva ponto e vírgula ali (quem leva, no fim, é o do-while).
#include <iostream>
int main() {
int numero = 5;
while (numero = 0) {
std::cout << numero << '\n';
numero--;
}
std::cout << "Fim\n";
}
$ g++ -std=c++23 -Wall -Wextra igual.cpp -o igual
igual.cpp: In function ‘int main()’:
igual.cpp:5:19: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
5 | while (numero = 0) {
| ~~~~~~~^~~
$ ./igual
Fim
O que significa: "sugiro parênteses em volta da atribuição usada como valor verdadeiro/falso". Um = sozinho é atribuição ("recebe"), não comparação. A condição numero = 0 guarda 0 em numero e vale 0, que conta como false: o laço não rodou nenhuma vez. Com numero = 1, valeria sempre true e o laço seria infinito.
Como consertar: para comparar, use == ou, como aqui, !=: while (numero != 0). O Clang do editor dá o mesmo aviso (using the result of an assignment as a condition without parentheses) e ainda sugere: use '==' to turn this assignment into an equality comparison.
#include <iostream>
int main() {
int n = 1;
while (n <= 3) {
int soma = 0;
soma += n;
std::cout << "Soma até agora: " << soma << '\n';
n++;
}
}
$ g++ -std=c++23 -Wall -Wextra zera.cpp -o zera $ ./zera Soma até agora: 1 Soma até agora: 2 Soma até agora: 3
O que aconteceu: o esperado era 1, 3, 6. Como int soma = 0; está dentro das chaves, uma nova soma, valendo 0, é criada em cada volta. O compilador não avisa: o programa é válido, só faz outra coisa.
Como consertar: mova int soma = 0; para antes do while. Regra prática: o que deve sobreviver entre uma volta e outra nasce fora do laço.
O que aconteceu: a condição já era falsa na primeira pergunta, como no exemplo do estoque zerado. Às vezes é o certo; às vezes é a condição invertida (> no lugar de <) ou o contador começando no valor errado.
Como investigar: mostre a variável antes do laço (std::cout << "i = " << i << '\n';) e faça a tabela do passo a passo para a primeira pergunta.
Referência
| Forma | O que faz |
|---|---|
while (condição) { … } | Testa a condição; se verdadeira, roda o corpo e testa de novo. Pode rodar 0 vezes. |
int i = 1; while (i <= N) { …; i++; } | Contador de 1 até N. |
int i = N; while (i > 0) { …; i--; } | Contagem regressiva de N até 1. |
int soma = 0; … soma += x; | Acumulador: nasce fora, cresce dentro. |
std::cin >> x; while (x != 0) { …; std::cin >> x; } | Ler até a sentinela (o 0). |
while (true) { … } | Laço infinito de propósito; sai com break (aula). |
| Ctrl+C | Interrompe um programa travado no terminal (código 130). |
timeout 3 ./programa | Roda com prazo de 3 segundos (código 124 se estourar). |
Exercícios
1. Complete a condição
Complete para o laço mostrar os números de 1 a 10:
2. O passo que falta
Complete o passo para esta contagem regressiva terminar:
3. Quanto vale no fim?
Depois de int i = 0; while (i < 4) { i++; }, quanto vale i?
4. Quantas vezes o corpo de int x = 10; while (x < 5) { x++; } roda?
5. int i = 1; while (i <= 5) { std::cout << i; } — o que acontece?
6. Você rodou timeout 3 ./jogo e depois echo $? mostrou 124. O que isso quer dizer?
Missão final: dobrar até passar de 1000
Missão: a variável valor começa em 1. Enquanto ela for menor que 1000, mostre o valor (um por linha) e dobre-o. O último número mostrado deve ser 512 (o próximo, 1024, já passa de 1000). Use um while.
#include <iostream>
int main() {
int valor = 1;
// escreva o while aqui
}
#include <iostream>
int main() {
int valor = 1;
while (valor < 1000) {
std::cout << valor << '\n';
valor *= 2;
}
}
Resumo
while (condição) { corpo }repete o corpo enquanto a condição for verdadeira. A condição é testada antes de cada volta, então o corpo pode rodar 0 vezes.- Todo laço com contador tem três peças: início (
int i = 1;), condição (i <= 5) e passo (i++). - Um acumulador (
soma) nasce fora do laço, valendo 0, e cresce dentro dele. - Para ler até uma sentinela (o 0): leia uma vez antes do laço e de novo no fim do corpo.
- Se nada muda a condição, o laço é infinito: pare com Ctrl+C e teste com
timeout 3(código 124). - Nada de
;logo depois dewhile ( … ), e=não é==: o-Wallavisa os dois.