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.
- As três partes do
fore 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:
- Começa a contar do 1 (isso acontece uma vez só, antes da primeira volta).
- Antes de cada volta, confere: já passei de 10? Se não, corre mais uma.
- 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
ide 1 até 5…). - int i = 1;
- 1ª parte, o início. Roda uma vez só, antes de tudo. Aqui cria o contador
ivalendo 1. - i <= 5;
- 2ª parte, a condição. Testada antes de cada volta, como no
while. Tem de darbool. Se forfalse, 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:
| Passo | O que roda | i depois |
|---|---|---|
| 1 | int i = 1 (início, só desta vez) | 1 |
| 2 | i <= 5? sim → corpo: Volta 1 → passo i++ | 2 |
| 3 | i <= 5? sim → corpo: Volta 2 → passo i++ | 3 |
| … | … | … |
| 6 | i <= 5? sim → corpo: Volta 5 → passo i++ | 6 |
| 7 | i <= 5? não → sai do laço | 6 |
Este for faz exatamente o mesmo que este while da aula anterior:
while | for |
|---|---|
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… | Escrevo | Resultado |
|---|---|---|
| De 1 a 10 | for (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 1 | for (int i = 10; i >= 1; i--) | 10 9 … 1 |
| Pares de 0 a 20 | for (int i = 0; i <= 20; i += 2) | 0 2 4 … 20 |
| De 5 em 5 | for (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çãoi.
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
- "Ana" tem 3 caracteres, nas posições 0, 1 e 2. As três primeiras voltas funcionaram.
- Com
<=, o laço deu uma quarta volta, comi = 3(3 <= 3étrue). nome[3]não existe. O C# não deixa ler fora dastring: ele lança a exceçãoIndexOutOfRangeException("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ção | Laç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ção | foreach |
| 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 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
| Forma | O 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:
2. Quantas voltas?
Quantas voltas dá for (int i = 0; i < 7; i++)?
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
whilecom 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 (
CS0103depois), 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: usei < x.Length. Com<=, vem aIndexOutOfRangeException(código 134). - As partes são separadas por
;(com vírgula:CS1002), e nada de;depois do)(CS0642).