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.

Você vai aprender
  • 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:

  1. Você olha a pia: tem louça? Sim → lava um prato.
  2. Olha de novo: tem louça? Sim → lava mais um.
  3. … e assim por diante …
  4. 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 ser volta ou contador.
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 i for 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, i seria 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:

MomentoValor de ii <= 5?O que acontece
Antes do laço1verdadeiroEntra: mostra Volta 1, e i vira 2
2ª pergunta2verdadeiroMostra Volta 2, i vira 3
3ª pergunta3verdadeiroMostra Volta 3, i vira 4
4ª pergunta4verdadeiroMostra Volta 4, i vira 5
5ª pergunta5verdadeiroMostra Volta 5, i vira 6
6ª pergunta6falsoSai 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.

As três peças de todo laço com contador

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.

Crie o acumulador fora do laço

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 while não teria um número para testar (veja Entrada com cin).
while (numero != 0)
"Enquanto o número for diferente de zero." O 0 funciona 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).

No editor do guia, a entrada já vem pronta

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).

Por que o 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.

No editor do guia

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:

  1. O que muda a cada volta? Alguma linha do corpo precisa mudar uma variável que aparece na condição (i++, segundos--, um novo std::cin >> numero).
  2. A mudança vai na direção certa? Com while (i <= 5), o i precisa subir. Um i-- por engano faria i descer para sempre.
  3. O valor de parada é alcançável? Com while (i != 10) e i += 3 começando do 1, o i passa 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çãoLaç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 listafor de intervalo

Quando dá errado

O programa trava e não termina (esqueceu o passo)
$ 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.

Um ponto e vírgula depois do 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).

"suggest parentheses around assignment used as truth value"
#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.

A soma não acumula (acumulador dentro do laço)
#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 laço não roda nenhuma vez

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

FormaO 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+CInterrompe um programa travado no terminal (código 130).
timeout 3 ./programaRoda 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:

int i = 1; while () { std::cout << i << '\n'; i++; }

2. O passo que falta

Complete o passo para esta contagem regressiva terminar:

int n = 3; while (n > 0) { std::cout << n << '\n'; }

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 de while ( … ), e = não é ==: o -Wall avisa os dois.