C# while

Imagine mostrar os números de 1 a 100 escrevendo cem Console.WriteLine. Ou somar as vendas de um mês inteiro, dia por dia. Computadores são ótimos para repetir, e as instruções que repetem um trecho de código se chamam laços (em inglês, loops). O mais simples deles é o while ("enquanto"): ele repete um bloco enquanto uma condição for verdadeira. Nesta aula você também vai ver o perigo de todo laço, o laço infinito, e como testá-lo sem medo.

Você vai aprender
  • Como o while funciona: testa a condição, roda o bloco, testa de novo…
  • As três peças de um laço com contador: início, condição e passo.
  • Como contar para trás e como acumular uma soma.
  • Como repetir quando você não sabe quantas vezes vai ser.
  • O laço infinito: por que acontece e como parar (Ctrl+C e timeout).
  • Os erros mais comuns, com as mensagens reais do compilador.

Todos os programas desta aula rodaram com o .NET 10 (C# 14) na máquina fictícia da Ana, na pasta ~/cs, com dotnet run arquivo.cs.


A ideia: "enquanto tiver louça, lave"

Depois do almoço, a Ana olha a pia: enquanto tiver louça suja, ela pega um prato e lava. Antes de cada prato, ela olha de novo. Quando a pia fica vazia, ela para e vai fazer outra coisa. Ela não precisa saber, antes de começar, quantos pratos são.

O while faz exatamente isso:

  1. Testa a condição ("tem louça?").
  2. Se for true, roda o bloco (lava um prato) e volta ao passo 1.
  3. Se for false, sai do laço e o programa continua depois dele.

Cada passagem pelo bloco se chama uma volta (ou iteração).


1. O primeiro while

int i = 1;
while (i <= 5)
{
    Console.WriteLine($"Volta {i}");
    i++;
}
Console.WriteLine($"Fim! i agora vale {i}");
$ dotnet run voltas.cs
Volta 1
Volta 2
Volta 3
Volta 4
Volta 5
Fim! i agora vale 6
int i = 1;
O início: cria o contador antes do laço. O nome i é tradição para contadores (de "índice").
while (i <= 5)
A condição, entre parênteses, como no if. Tem de dar bool. É testada antes de cada volta.
{ … }
O corpo do laço: o que se repete. Chaves sempre, pelo mesmo motivo da aula de if e else.
i++;
O passo: soma 1 ao contador (aula ++ e --). Sem ele, i ficaria valendo 1 para sempre (seção 5).
Fim! i agora vale 6
O laço parou quando i chegou a 6, porque 6 <= 5 é false. Por isso, depois do laço, i vale 6, e não 5.

Volta por volta

Testei antesi <= 5?O que acontece
1º1truemostra Volta 1, i vira 2
2º2truemostra Volta 2, i vira 3
3º3truemostra Volta 3, i vira 4
4º4truemostra Volta 4, i vira 5
5º5truemostra Volta 5, i vira 6
6º6falsesai do laço

Cinco voltas, mas seis testes: o último teste é o que dá false e encerra o laço.

Missão: o programa mostra a tabuada do 7, mas só até o 3. Mude o que for preciso para ele ir de 7 x 1 até 7 x 10.

7 x 1 = 7
7 x 2 = 14
…
7 x 10 = 70
int i = 1;
while (i <= 3)
{
    Console.WriteLine($"7 x {i} = {7 * i}");
    i++;
}
int i = 1;
while (i <= 10)
{
    Console.WriteLine($"7 x {i} = {7 * i}");
    i++;
}

2. Contar para trás

Para uma contagem regressiva, o contador começa no maior valor, a condição usa >= e o passo é --:

int s = 10;
while (s >= 1)
{
    Console.Write($"{s} ");
    s--;
}
Console.WriteLine("Feliz Ano Novo!");
$ dotnet run regressiva.cs
10 9 8 7 6 5 4 3 2 1 Feliz Ano Novo!

O Console.Write (sem o "Line") não pula linha, por isso os números ficaram lado a lado, cada um seguido de um espaço (aula Saída).


3. Acumular: a soma de 1 a 100

Um laço também serve para juntar resultados. A ideia é ter uma variável "acumuladora" que começa em 0 e recebe um pouco a cada volta, como um cofrinho:

int n = 1;
int soma = 0;
while (n <= 100)
{
    soma += n;
    n++;
}
Console.WriteLine($"1 + 2 + ... + 100 = {soma}");
$ dotnet run soma.cs
1 + 2 + ... + 100 = 5050
int soma = 0;
O acumulador começa em zero, antes do laço. Se fosse criado dentro do laço, voltaria a zero a cada volta.
soma += n;
O mesmo que soma = soma + n; (aula Operadores de Atribuição). Na 1ª volta, soma vira 1; na 2ª, 3; na 3ª, 6; …
{soma}
O resultado é mostrado depois do laço, uma vez só.

4. Quando você não sabe quantas voltas

Até aqui, dava para saber de antemão quantas voltas seriam (5, 10, 100). Para isso, a próxima aula, for, é até mais prática. O while brilha quando o número de voltas depende do que acontece dentro do laço. A Ana guarda R$ 150 por mês e quer saber em quantos meses chega a R$ 1.000:

decimal guardado = 0m;
decimal porMes = 150m;
decimal meta = 1000m;
int meses = 0;
while (guardado < meta)
{
    guardado += porMes;
    meses++;
    Console.WriteLine($"Mês {meses}: R$ {guardado:N2}");
}
Console.WriteLine($"Meta atingida em {meses} meses!");
$ dotnet run poupanca.cs
Mês 1: R$ 150,00
Mês 2: R$ 300,00
Mês 3: R$ 450,00
Mês 4: R$ 600,00
Mês 5: R$ 750,00
Mês 6: R$ 900,00
Mês 7: R$ 1.050,00          « 1.050 não é < 1.000: o laço para aqui
Meta atingida em 7 meses!

Ninguém escreveu "7" no programa: o laço descobriu sozinho. Mude porMes para 200m e ele para no mês 5; mude a meta para 5000m e ele vai até o mês 34. O dinheiro está em decimal, como sempre neste guia (aula float, double e decimal).

O laço que não roda nenhuma vez

Como a condição é testada antes da primeira volta, se ela já começa false, o corpo nunca roda:

int estoque = 0;
while (estoque > 0)
{
    Console.WriteLine("Vendendo uma unidade...");
    estoque--;
}
Console.WriteLine("Estoque vazio.");
$ dotnet run zero.cs
Estoque vazio.          « 0 > 0 é false: zero voltas

Isso é uma qualidade: o while nunca faz uma volta "a mais" quando não devia. Quando você precisa que o corpo rode pelo menos uma vez (por exemplo, para mostrar um menu antes de perguntar se a pessoa quer sair), existe o do while, a próxima aula.


5. 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++: i vale 1 para sempre, e 1 <= 5 é sempre true.

int i = 1;
Console.WriteLine("Calculando os dobros...");
while (i <= 5)
{
    int dobro = i * 2;
}
Console.WriteLine("Pronto!");

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 Calculando os dobros... e fica parado, com o processador trabalhando sem parar, sem nunca chegar ao Pronto!. No terminal, aperte Ctrl+C para interromper o programa.

Como testar sem medo: timeout

Quando você suspeita que um programa pode travar, rode-o com o comando timeout do Linux, que dá um prazo em segundos e encerra o programa se ele passar disso. Foi assim que os laços infinitos deste guia foram testados. Dê um prazo folgado: o primeiro dotnet run de um arquivo novo gasta alguns segundos só para compilar.

$ timeout 15 dotnet run infinito.cs
Calculando os dobros...           « e nada mais por 15 segundos
$ echo $?
124

O echo $? mostra o código de saída do último comando. O 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).

No editor do guia

Um laço infinito também não termina sozinho na caixa "Experimente". Enquanto o programa roda, o botão Rodar vira ■ Parar: clique nele, e o terminal da caixa mostra ^C e [código de saída: 130], como o Ctrl+C no terminal. Se você não parar, o editor para o programa sozinho depois de 15 segundos e avisa que ele rodou demais (laço infinito?). Antes de rodar, confira se o laço tem um passo que leva a condição a ficar falsa.

Laço infinito de propósito: while (true)

Às vezes é mais fácil escrever "repita para sempre" e decidir a saída lá dentro, com break ("quebrar"), que sai do laço na hora:

int vidas = 3;
while (true)
{
    Console.WriteLine($"Vidas: {vidas}");
    vidas--;
    if (vidas == 0)
    {
        break;
    }
}
Console.WriteLine("Fim de jogo.");
$ dotnet run dado.cs
Vidas: 3
Vidas: 2
Vidas: 1
Fim de jogo.

O break e o seu parente continue têm uma aula só para eles: break e continue.


6. O ; depois do while

A armadilha do if (…); também existe aqui, e é pior: vira um laço infinito silencioso.

int i = 1;
while (i <= 5);
{
    Console.WriteLine($"Volta {i}");
    i++;
}
Console.WriteLine("Fim!");
$ timeout 15 dotnet run pv.cs
/home/ana/cs/pv.cs(2,15): warning CS0642: Instrução empty possivelmente incorreta
$ echo $?
124                « nem "Volta 1" apareceu
  1. O ; logo depois do ) é uma instrução vazia, e ela virou o corpo do laço: "enquanto i <= 5, não faça nada".
  2. Como o corpo não faz nada, i nunca muda, e o laço nunca termina.
  3. O bloco { … } de baixo ficou fora do laço e nunca é alcançado.

O aviso CS0642 aponta a coluna 15 da linha 2: o ;. Toda vez que vir esse aviso, apague o ponto e vírgula.


Formas diferentes + qual usar

LaçoTesta a condiçãoQuando usar
while (cond) { … }Antes de cada volta (pode rodar 0 vezes)Quando o número de voltas depende do que acontece: "até juntar R$ 1.000", "enquanto houver itens".
do { … } while (cond);Depois de cada volta (roda pelo menos 1 vez)Menus e perguntas que precisam aparecer antes do teste. Aula do while.
for (int i = 0; i < n; i++) { … }Antes de cada voltaQuando você sabe quantas voltas: de 1 a 10, cada posição de um array. Aula for.
foreach (var x in lista) { … }(não tem condição)Passar por todos os itens de uma coleção. Aula foreach.
while (true) { … break; … }Dentro, com breakQuando a decisão de sair fica no meio do corpo.

Quando dá errado

O programa não termina (laço infinito)

O que significa: a condição nunca fica false. Causas comuns: esqueceu o passo (i++), o passo vai para o lado errado (i-- numa condição i <= 5), ou há um ; depois do while (…).

Como consertar: aperte Ctrl+C. Depois confira as três peças: o contador começa certo? A condição é a certa? O passo existe, está dentro das chaves e leva a condição a ficar falsa?

warning CS0642: Instrução empty possivelmente incorreta

O que significa: há um ; logo depois do ) do while. O laço tem corpo vazio e, quase sempre, nunca termina.

Como consertar: apague o ;.

error CS0029: Não é possível converter implicitamente tipo "int" em "bool"
$ cat intcond.cs
int vidas = 3;
while (vidas)
{
    vidas--;
}
$ dotnet run intcond.cs
/home/ana/cs/intcond.cs(2,8): error CS0029: Não é possível converter implicitamente tipo "int" em "bool"

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: como no if, a condição do while tem de ser bool. Um número sozinho não serve.

Como consertar: while (vidas > 0).

error CS0165: Uso de variável local não atribuída "i"
$ cat semvalor.cs
int i;
while (i <= 3)
{
    Console.WriteLine(i);
    i++;
}
$ dotnet run semvalor.cs
/home/ana/cs/semvalor.cs(2,8): error CS0165: Uso de variável local não atribuída "i"

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: o contador foi declarado sem valor inicial, e a condição tenta lê-lo. Em C, isso compilaria (no máximo com um aviso) e o contador começaria com um valor qualquer, o "lixo" da memória; o C# recusa.

Como consertar: int i = 1; (ou o valor de início que você quiser).

error CS0103: O nome "quadrado" não existe no contexto atual
$ cat dentro.cs
int i = 1;
while (i <= 3)
{
    int quadrado = i * i;
    i++;
}
Console.WriteLine(quadrado);
$ dotnet run dentro.cs
/home/ana/cs/dentro.cs(7,19): error CS0103: O nome "quadrado" não existe no contexto atual

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: uma variável criada dentro das chaves do laço só existe lá dentro (e é criada de novo a cada volta).

Como consertar: se você precisa do valor depois do laço, declare a variável antes dele: int quadrado = 0;, e dentro do laço só faça quadrado = i * i;.

O laço dá uma volta a mais ou a menos

O que significa: o famoso erro "por um" (em inglês, off-by-one): < no lugar de <=, ou o contador começando em 0 em vez de 1. i = 1; while (i < 5) dá só 4 voltas.

Como consertar: faça a tabela "volta por volta" da seção 1 para os primeiros e os últimos valores.


Referência rápida

FormaO que faz
while (cond) { … }Repete o bloco enquanto cond for true. Testa antes de cada volta.
int i = 1; while (i <= n) { …; i++; }Conta de 1 a n.
int i = n; while (i >= 1) { …; i--; }Conta de n até 1.
soma += x; dentro do laçoAcumula (com soma criada antes, valendo 0).
while (true) { … if (…) { break; } }Repete até um break.
Ctrl+CInterrompe o programa no terminal.
timeout 15 dotnet run x.csRoda com prazo de 15 segundos (código 124 se estourar).

Exercícios

1. Enquanto

Complete com a palavra do laço:

int i = 1; (i <= 3) { Console.WriteLine(i); i++; }

2. Quantas voltas?

Quantas vezes o corpo deste laço roda? int i = 0; while (i < 4) { i++; }

Voltas:

3. O passo

Complete o passo para a contagem regressiva terminar:

int s = 3; while (s >= 1) { Console.WriteLine(s); s; }

4. O que acontece com int i = 1; while (i <= 5) { Console.WriteLine(i); }?

5. Com int n = 10;, quantas vezes roda while (n < 5) { n++; }?

6. O que quer dizer o código de saída 124 depois de timeout 15 dotnet run x.cs?

Missão final: a poupança da Ana

Missão: a Ana guarda R$ 150 por mês e quer chegar a R$ 1.000. Escreva um while que, enquanto o guardado for menor que a meta, some o valor do mês ao guardado e conte mais um mês. (Cuidado: se esquecer o guardado += porMes;, o laço fica infinito e você vai ter que recarregar a página.) A saída deve ser:

Meta atingida em 7 meses: R$ 1.050,00
decimal guardado = 0m;
decimal porMes = 150m;
decimal meta = 1000m;
int meses = 0;

// escreva o while aqui

Console.WriteLine($"Meta atingida em {meses} meses: R$ {guardado:N2}");
decimal guardado = 0m;
decimal porMes = 150m;
decimal meta = 1000m;
int meses = 0;

while (guardado < meta)
{
    guardado += porMes;
    meses++;
}

Console.WriteLine($"Meta atingida em {meses} meses: R$ {guardado:N2}");

Depois, mude o porMes para 275m: em quantos meses ela chega lá? E se a meta for 0m? (Zero meses: o laço não roda nenhuma vez.)


Resumo

  • while (condição) { … } repete o bloco enquanto a condição for true. A condição é testada antes de cada volta, então o laço pode rodar 0 vezes.
  • Laço com contador: início antes (int i = 1;), condição nos parênteses, passo no fim do corpo (i++).
  • Para acumular, crie o acumulador antes do laço, valendo 0, e some dentro dele.
  • O while é o laço certo quando o número de voltas depende do que acontece dentro dele.
  • Sem o passo, ou com ; depois do while (…) (CS0642), o laço é infinito. Pare com Ctrl+C; teste com timeout (código 124).
  • A condição tem de ser bool (CS0029), o contador precisa de valor inicial (CS0165) e variáveis criadas no corpo não existem depois dele (CS0103).