C# for

Na aula do while, todo laço com contador tinha três peças espalhadas: o início antes do laço (int i = 1;), a condição nos parênteses (i <= 5) e o passo no fim do corpo (i++). O for ("para") junta as três numa linha só. É o laço preferido quando você sabe quantas voltas quer: de 1 a 10, 12 meses, cada letra de um nome, cada posição de um array.

Você vai aprender
  • As três partes do for e a ordem em que elas rodam.
  • Por que o contador só existe dentro do laço (CS0103), e o erro do nome repetido (CS0136).
  • Como contar para trás, de 2 em 2 e com qualquer passo.
  • Como fazer um somatório, uma tabuada e percorrer as letras de uma string.
  • O erro mais comum de todos os laços: o "um a mais" do <=, que aqui vira uma exceção.

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: "dê 10 voltas na quadra"

Um professor de educação física diz: "deem 10 voltas na quadra". Cada aluno faz, sem pensar, três coisas:

  1. Começa a contar do 1 (isso acontece uma vez só, antes da primeira volta).
  2. Antes de cada volta, confere: já passei de 10? Se não, corre mais uma.
  3. No fim de cada volta, soma um na contagem.

O for escreve exatamente isso, nessa ordem, entre os parênteses: for (começa; confere; soma). Olhando só a primeira linha, você já sabe quantas voltas o laço vai dar.


1. O primeiro for

for (int i = 1; i <= 5; i++)
{
    Console.WriteLine($"Volta {i}");
}
Console.WriteLine("Fim!");
$ dotnet run for1.cs
Volta 1
Volta 2
Volta 3
Volta 4
Volta 5
Fim!
for
A palavra-chave do laço: "para" (para i de 1 até 5…).
int i = 1;
1ª parte, o início. Roda uma vez só, antes de tudo. Aqui cria o contador i valendo 1.
i <= 5;
2ª parte, a condição. Testada antes de cada volta, como no while. Tem de dar bool. Se for false, o laço termina.
i++
3ª parte, o passo. Roda no fim de cada volta, depois do corpo. Repare que não tem ; depois dele.
{ … }
O corpo, como em qualquer laço. Chaves sempre.

A ordem exata

As partes estão escritas lado a lado, mas não rodam da esquerda para a direita a cada volta. A ordem é esta:

PassoO que rodai depois
1int i = 1 (início, só desta vez)1
2i <= 5? sim → corpo: Volta 1 → passo i++2
3i <= 5? sim → corpo: Volta 2 → passo i++3
………
6i <= 5? sim → corpo: Volta 5 → passo i++6
7i <= 5? não → sai do laço6

Este for faz exatamente o mesmo que este while da aula anterior:

whilefor
int i = 1;
while (i <= 5)
{
    Console.WriteLine($"Volta {i}");
    i++;
}
for (int i = 1; i <= 5; i++)
{
    Console.WriteLine($"Volta {i}");
}

A vantagem do for: as três peças ficam juntas no topo, então fica mais difícil esquecer o passo (o laço infinito do while) e mais fácil ver quantas voltas serão.


2. O contador só existe dentro do for

Uma diferença importante para a versão com while: o i criado dentro dos parênteses do for pertence ao laço. Depois dele, não existe mais:

for (int i = 1; i <= 5; i++)
{
    Console.WriteLine($"Volta {i}");
}
Console.WriteLine($"i parou em {i}");
$ dotnet run depois.cs
/home/ana/cs/depois.cs(5,33): error CS0103: O nome "i" não existe no contexto atual

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

Isso é uma qualidade: o contador não "vaza" para o resto do programa, e você pode usar i de novo num próximo for. Se você precisa saber onde o laço parou, declare a variável antes, e o for só dá o valor inicial:

int i;
for (i = 1; i <= 5; i++)
{
    Console.WriteLine($"Volta {i}");
}
Console.WriteLine($"i parou em {i}");
$ dotnet run fora.cs
Volta 1
Volta 2
Volta 3
Volta 4
Volta 5
i parou em 6

O contrário também é proibido: se já existe um i fora, o for não pode criar outro i:

int i = 0;
for (int i = 1; i <= 3; i++)
{
    Console.WriteLine(i);
}
$ dotnet run dupla.cs
/home/ana/cs/dupla.cs(2,10): error CS0136: Um local ou um parâmetro denominado "i" não pode ser declarado neste escopo porque esse nome é usado em um escopo delimitador de local para definir um local ou parâmetro
/home/ana/cs/dupla.cs(1,5): warning CS0219: A variável "i" é atribuída, mas seu valor nunca é usado

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

A mensagem é comprida, mas diz: "não dá para criar um i aqui, porque o nome i já é usado no escopo de fora". Em C isso seria permitido (o i de dentro esconderia o de fora); o C#, como o Java, proíbe para evitar confusão. Conserto: escolha outro nome, ou use o i que já existe (for (i = 1; …)).


3. Contar para trás e mudar o passo

As três partes aceitam qualquer coisa que faça sentido. Para contar para trás: comece do maior, teste "maior ou igual" e use --:

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

Para andar de 2 em 2, troque o passo por n += 2 (aula Operadores de Atribuição). Começando do 0, saem os números pares:

for (int n = 0; n <= 20; n += 2)
{
    Console.Write($"{n} ");
}
Console.WriteLine();
$ dotnet run pares.cs
0 2 4 6 8 10 12 14 16 18 20 

O Console.WriteLine() vazio, no fim, só pula a linha depois dos números.

Quero…EscrevoResultado
De 1 a 10for (int i = 1; i <= 10; i++)1 2 3 … 10
De 0 a 9 (10 voltas)for (int i = 0; i < 10; i++)0 1 2 … 9
De 10 a 1for (int i = 10; i >= 1; i--)10 9 … 1
Pares de 0 a 20for (int i = 0; i <= 20; i += 2)0 2 4 … 20
De 5 em 5for (int i = 5; i <= 50; i += 5)5 10 … 50

Missão: mude o for para mostrar os múltiplos de 5, de 5 até 50, na mesma linha:

5 10 15 20 25 30 35 40 45 50 
for (int n = 0; n <= 20; n += 2)
{
    Console.Write($"{n} ");
}
Console.WriteLine();
for (int n = 5; n <= 50; n += 5)
{
    Console.Write($"{n} ");
}
Console.WriteLine();

4. Somatório e tabuada

Os mesmos truques do while, agora mais curtos. A soma de 1 a 100, com o acumulador criado antes do laço:

int soma = 0;
for (int n = 1; n <= 100; n++)
{
    soma += n;
}
Console.WriteLine($"Soma de 1 a 100: {soma}");
$ dotnet run soma.cs
Soma de 1 a 100: 5050

E a tabuada do 9, bem alinhada:

int numero = 9;
for (int i = 1; i <= 10; i++)
{
    Console.WriteLine($"{numero} x {i,2} = {numero * i,3}");
}
$ dotnet run tabuada.cs
9 x  1 =   9
9 x  2 =  18
9 x  3 =  27
9 x  4 =  36
9 x  5 =  45
9 x  6 =  54
9 x  7 =  63
9 x  8 =  72
9 x  9 =  81
9 x 10 =  90

O {i,2} reserva 2 espaços para o número, alinhado à direita, e o {numero * i,3} reserva 3 (aula Interpolação). Sem isso, o 10 e o 90 empurrariam a última linha para a direita.


5. Percorrer as letras de uma string

Uma string é uma sequência de caracteres, e cada um tem uma posição (índice), que começa em 0. nome[0] é o primeiro caractere, e nome.Length é quantos são (aula Strings). Com um for de 0 até Length - 1, você visita todos:

string nome = "Ana Lima";
for (int i = 0; i < nome.Length; i++)
{
    Console.WriteLine($"Posição {i}: '{nome[i]}'");
}
Console.WriteLine($"Total: {nome.Length} caracteres");
$ dotnet run letras.cs
Posição 0: 'A'
Posição 1: 'n'
Posição 2: 'a'
Posição 3: ' '
Posição 4: 'L'
Posição 5: 'i'
Posição 6: 'm'
Posição 7: 'a'
Total: 8 caracteres
int i = 0
Começa no 0, a primeira posição.
i < nome.Length
Com <, e não <=: são 8 caracteres, nas posições 0 a 7. A posição 8 não existe.
nome[i]
O caractere (char) da posição i.

Esse mesmo padrão, for (int i = 0; i < x.Length; i++), é o jeito de percorrer um array. Quando você só quer cada item, sem precisar da posição, o foreach da próxima aula é mais simples.

Com um if dentro do for, dá para contar coisas. Quantas vogais tem a frase?

string frase = "programar em csharp";
int vogais = 0;
for (int i = 0; i < frase.Length; i++)
{
    char c = frase[i];
    if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
    {
        vogais++;
    }
}
Console.WriteLine($"\"{frase}\" tem {vogais} vogais.");
$ dotnet run vogais.cs
"programar em csharp" tem 5 vogais.

6. O erro do "um a mais"

O erro mais comum de todos os laços é dar uma volta a mais (ou a menos). Em inglês ele tem até nome: off-by-one. Com posições, a volta a mais tenta ler uma posição que não existe:

string nome = "Ana";
for (int i = 0; i <= nome.Length; i++)
{
    Console.WriteLine(nome[i]);
}
$ dotnet run umamais.cs
A
n
a
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.String.get_Chars(Int32 index)
   at Program.<Main>$(String[] args) in /home/ana/cs/umamais.cs:line 4
$ echo $?
134
  1. "Ana" tem 3 caracteres, nas posições 0, 1 e 2. As três primeiras voltas funcionaram.
  2. Com <=, o laço deu uma quarta volta, com i = 3 (3 <= 3 é true).
  3. nome[3] não existe. O C# não deixa ler fora da string: ele lança a exceção IndexOutOfRangeException ("o índice estava fora dos limites"), aponta a linha 4, e o programa para com o código 134.

A linha at System.String.get_Chars(Int32 index) mostra onde, dentro do .NET, o erro foi descoberto (no código que lê um caractere da string); no editor do guia ela não aparece, mas a linha que importa, a line 4 do seu programa, aparece nos dois.

O compilador não tem como ver esse erro: ele só aparece ao rodar. Em C, o programa leria um pedaço qualquer da memória e seguiria em frente, em silêncio; o C# prefere parar e avisar. A regra: posições vão de 0 a Length - 1, então use i < nome.Length.

Missão: rode e veja a exceção. Depois conserte a condição para o programa mostrar as três letras e terminar bem (código de saída 0).

A
n
a
string nome = "Ana";
for (int i = 0; i <= nome.Length; i++)
{
    Console.WriteLine(nome[i]);
}
string nome = "Ana";
for (int i = 0; i < nome.Length; i++)
{
    Console.WriteLine(nome[i]);
}

7. Outras formas do for

Duas variáveis

O início e o passo aceitam várias partes separadas por vírgula. Aqui, i sobe e j desce até se encontrarem:

for (int i = 0, j = 10; i < j; i += 2, j -= 2)
{
    Console.WriteLine($"i = {i}, j = {j}");
}
$ dotnet run dois.cs
i = 0, j = 10
i = 2, j = 8
i = 4, j = 6

Funciona, mas é raro. Se o for ficar difícil de ler, um while pode ser mais claro.

Partes vazias e for (;;)

As três partes são opcionais (os dois ;, não). for (;;), sem nada, é um laço infinito, o mesmo que while (true). Saia dele com break (aula break e continue). Prefira while (true), que se lê melhor.


Qual laço usar?

SituaçãoLaço
Você sabe quantas voltas (de 1 a 10, 12 meses)for
Você precisa da posição de cada item (nome[i], "posição 3")for
Você quer cada item de uma coleção, sem a posiçãoforeach
O número de voltas depende do que acontece ("até juntar R$ 1.000")while
A primeira volta é obrigatória (menu, pedir de novo)do while

Quando dá errado

Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.

O que significa: o laço tentou ler uma posição que não existe, quase sempre por causa de um <= no lugar de <. A linha no fim da mensagem (line 4) mostra onde.

Como consertar: for (int i = 0; i < x.Length; i++).

error CS0103: O nome "i" não existe no contexto atual depois do laço

O que significa: o contador foi criado nos parênteses do for e só existe dentro dele.

Como consertar: declare antes do for (int i; for (i = 1; …)) se precisar do valor depois.

error CS0136: Um local ou um parâmetro denominado "i" não pode ser declarado neste escopo…

O que significa: já existe uma variável i fora do for, e o for tentou criar outra com o mesmo nome.

Como consertar: outro nome para o contador, ou for (i = 1; …) sem o int.

error CS1002: ; esperado dentro dos parênteses
$ cat virgula.cs
for (int i = 1, i <= 3, i++)
{
    Console.WriteLine(i);
}
$ dotnet run virgula.cs
/home/ana/cs/virgula.cs(1,15): error CS1002: ; esperado
/home/ana/cs/virgula.cs(1,23): error CS1002: ; esperado

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

O que significa: as três partes do for são separadas por ponto e vírgula, não por vírgula. O compilador aponta onde esperava cada ; (colunas 15 e 23).

Como consertar: for (int i = 1; i <= 3; i++).

warning CS0642: Instrução empty possivelmente incorreta
$ cat pv.cs
for (int i = 1; i <= 3; i++);
{
    Console.WriteLine("Olá!");
}
$ dotnet run pv.cs
/home/ana/cs/pv.cs(1,29): warning CS0642: Instrução empty possivelmente incorreta
Olá!

O que significa: o ; depois do ) virou o corpo do laço. O laço deu 3 voltas sem fazer nada, e o bloco de baixo rodou uma vez só, depois.

Como consertar: apague o ;.

O laço não roda nenhuma vez, ou nunca termina

O que significa: a condição e o passo não combinam. for (int i = 10; i <= 1; i--) não roda (10 já não é <= 1); for (int i = 1; i <= 10; i--) praticamente nunca termina (o i vai para 0, −1, −2… e continua sempre <= 10).

Como consertar: contando para cima, </<= com ++; para baixo, >/>= com --.


Referência rápida

FormaO que faz
for (início; condição; passo) { … }Início uma vez; condição antes de cada volta; passo no fim de cada volta.
for (int i = 1; i <= n; i++)De 1 a n.
for (int i = 0; i < x.Length; i++)Cada posição de uma string ou array.
for (int i = n; i >= 1; i--)De n até 1.
for (…; …; i += 2)De 2 em 2.
for (int i = 0, j = 10; …; i++, j--)Duas variáveis.
for (;;)Laço infinito (prefira while (true)).

Exercícios

1. As três partes

Complete o passo para contar de 1 a 10:

for (int i = 1; i <= 10; )

2. Quantas voltas?

Quantas voltas dá for (int i = 0; i < 7; i++)?

Voltas:

3. O separador

Qual caractere separa as três partes dentro dos parênteses do for?

4. Em que ordem rodam as partes de for (A; B; C) { D }?

5. Com string s = "oi";, o que acontece em for (int i = 0; i <= s.Length; i++) { Console.WriteLine(s[i]); }?

6. Por que for (int i = 0; i < 3; i++) { } Console.WriteLine(i); não compila?

Missão final: a poupança rendendo

Missão: a Ana deixou R$ 1.000 numa aplicação que rende 1% ao mês. O programa calcula só um mês. Ponha a linha do rendimento dentro de um for que dê 12 voltas, uma por mês (o rendimento de cada mês é calculado sobre o saldo do mês anterior: são os "juros compostos"). A saída deve ser:

Depois de 12 meses: R$ 1.126,83
decimal saldo = 1000m;
decimal taxa = 0.01m;

saldo += saldo * taxa;

Console.WriteLine($"Depois de 12 meses: R$ {saldo:N2}");
decimal saldo = 1000m;
decimal taxa = 0.01m;

for (int mes = 1; mes <= 12; mes++)
{
    saldo += saldo * taxa;
}

Console.WriteLine($"Depois de 12 meses: R$ {saldo:N2}");

Depois, mostre o saldo de cada mês, com um Console.WriteLine($"Mês {mes}: R$ {saldo:N2}"); dentro do laço. E compare: 12 × 1% = 12%, mas o saldo cresceu 12,68%. É o efeito dos juros sobre juros.


Resumo

  • for (início; condição; passo) { … }: o início roda uma vez; a condição, antes de cada volta; o passo, no fim de cada volta.
  • É o mesmo que um while com contador, com as três peças juntas no topo. Use quando souber quantas voltas.
  • O contador criado nos parênteses só existe dentro do laço (CS0103 depois), e não pode repetir um nome de fora (CS0136).
  • Para trás: i-- com >=. Outros passos: i += 2, i += 5.
  • Posições vão de 0 a Length - 1: use i < x.Length. Com <=, vem a IndexOutOfRangeException (código 134).
  • As partes são separadas por ; (com vírgula: CS1002), e nada de ; depois do ) (CS0642).