Menu

Laço for em C#: sintaxe, contagem regressiva, laços aninhados e armadilhas

Como o laço for funciona em C#: inicializador, condição e iterador, contagem regressiva e passos, várias variáveis de controle, percorrer arrays por índice, laços aninhados, for(;;), e os bugs de off by one e de remover itens durante a iteração.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um laço for repete um bloco enquanto uma condição for verdadeira, com a preparação e o passo escritos no cabeçalho do laço. É o laço para quando você conta: um número fixo de repetições, ou cada índice de um array.

Saída:

Lap 1 of 5
Lap 2 of 5
Lap 3 of 5
Lap 4 of 5
Lap 5 of 5

Como as três partes executam

for (initializer; condition; iterator) executa em uma ordem fixa:

  1. O inicializador (int i = 1) executa uma vez, antes de tudo.
  2. A condição (i <= 5) é verificada. Se for falsa, o laço termina e o corpo não executa mais.
  3. O corpo executa.
  4. O iterador (i++) executa, e o controle volta ao passo 2.

Como a condição é verificada antes de cada iteração, um laço for pode executar zero vezes: for (int i = 10; i < 5; i++) nunca entra no corpo. O laço acima se comporta como este laço while:

int i = 1;
while (i <= 5)
{
    Console.WriteLine($"Lap {i} of 5");
    i++;
}

As duas formas diferem em um detalhe: continue dentro do corpo de um for ainda executa o iterador, enquanto nesta versão com while ele pularia o i++, então o laço testaria o mesmo i de novo e poderia girar para sempre. Uma variável declarada no inicializador só existe dentro do laço. Se você precisa do valor final depois, declare-a antes do laço: int i; for (i = 0; ...; i++) { }.

Contagem regressiva e passos

O iterador pode ser qualquer expressão. Decremente para contar para baixo, some mais de um para avançar em passos, ou multiplique para uma sequência geométrica:

Saída:

3... 2... 1... liftoff
 00 15 30 45
 1 10 100 1000

Garanta que o iterador leve a condição a ficar falsa. for (int i = 0; i < 10; i--) se afasta de 10 e só para quando i dá a volta depois de dois bilhões de iterações.

Percorrendo um array por índice

Arrays expõem Length, listas expõem Count, e os índices válidos vão de 0 até um a menos que isso. O índice dá algo que o foreach não dá: acesso à posição, aos vizinhos e o direito de escrever de volta.

Saída:

#1: 17.99
#2: 4.50
#3: 38.25
#4: 7.43
item 1 vs item 0: down
item 2 vs item 1: up
item 3 vs item 2: down

O último laço começa em 1 porque compara cada elemento com o anterior. A página de arrays cobre como criar, ordenar e copiar os próprios arrays.

Várias variáveis de controle

O inicializador pode declarar várias variáveis do mesmo tipo, e o iterador pode atualizar várias, separadas por vírgulas. O uso clássico são dois índices andando um em direção ao outro:

Saída:

racecar is a palindrome: True
9, 1, 8, 3

As duas variáveis precisam ter o mesmo tipo em uma única declaração: int left = 0, right = 10 funciona, int i = 0, string s = "" não.

Laços aninhados

Um laço dentro de outro percorre todo o seu intervalo uma vez por iteração do laço externo. Tabelas e grades são o caso típico:

Saída:

   1   2   3   4
   2   4   6   8
   3   6   9  12
   4   8  12  16

O corpo interno executa 16 vezes (4 × 4). Aninhar multiplica o trabalho rapidamente: dois laços aninhados sobre 10.000 itens cada são 100 milhões de iterações. Um break no laço interno sai só do laço interno; a página de break e continue mostra como sair dos dois.

Laços infinitos: for(;;)

As três partes do cabeçalho são opcionais. Sem condição, o laço executa até que algo dentro dele saia:

Saída:

Month 1: 70 left
Month 2: 40 left
Month 3: 10 left
Overdrawn in month 4: -20

for (;;) e while (true) compilam para a mesma coisa; escolha o que o seu código já usa.

Erros off by one

O bug mais comum em um for é uma condição que vai um passo longe demais:

Saída:

Ada
Grace
Linus
IndexOutOfRangeException at index 3

Um array de 3 elementos tem os índices 0, 1 e 2, então i < names.Length é a condição certa. O bug espelhado é contar para baixo com i > 0, que nunca visita o índice 0. Em uma List<T>, o mesmo erro lança ArgumentOutOfRangeException no lugar.

Removendo itens durante o laço

Remover de uma List<T> dentro de um laço para frente desloca todos os elementos seguintes uma posição para baixo, então o laço pula o elemento logo depois de cada remoção:

Saída:

forward:  5, 0, 3, 7
backward: 5, 3, 7

O laço para frente deixou um 0 para trás: depois de remover o índice 1, o segundo zero passou para o índice 1, mas o i++ já tinha avançado para o índice 2. Percorrer de trás para frente evita isso porque as remoções só deslocam elementos que você já visitou. Como alternativa de uma linha, stock.RemoveAll(x => x == 0) faz a mesma coisa. Um laço foreach não permite remoção nenhuma; ele lança InvalidOperationException.

Contadores de ponto flutuante

Evite contadores double que testam igualdade. 0.1 não tem representação binária exata, então somá-lo dez vezes não produz exatamente 1.0:

for (double x = 0; x != 1.0; x += 0.1)   // never ends: x skips past 1.0
{
}

Conte com um inteiro e calcule o valor a partir dele: for (int i = 0; i <= 10; i++) { double x = i / 10.0; }.

for versus foreach

NecessidadeUse
Ler todos os elementos em ordemforeach
O índice, ou os vizinhos (i - 1, i + 1)for
Atribuir a um elemento do arrayfor
Avançar de 2 em 2, ir de trás para frentefor
Remover de uma List durante o laçofor, de trás para frente
Uma coleção sem indexador (HashSet, Dictionary, resultados de LINQ)foreach
Repetir algo N vezes, sem coleçãofor

Em arrays, os dois laços compilam para praticamente o mesmo código, então escolha pela legibilidade, não pela velocidade.

Perguntas frequentes

Qual é a sintaxe de um laço for em C#?

for (initializer; condition; iterator) { body }, por exemplo for (int i = 0; i < 5; i++) { ... }. O inicializador executa uma vez, a condição é verificada antes de cada iteração e o iterador executa depois de cada iteração. O laço termina na primeira vez em que a condição é falsa.

Como percorrer de trás para frente em C#?

Comece no último índice e decremente: for (int i = items.Length - 1; i >= 0; i--). Repare no >= 0, não > 0, senão o primeiro elemento é pulado. Percorrer de trás para frente também é a forma segura de remover itens de uma List durante a iteração.

Quando usar for em vez de foreach em C#?

Use for quando você precisa do índice (para comparar vizinhos, escrever de volta em um array ou avançar mais de um por vez), quando você conta em vez de percorrer uma coleção, ou quando remove itens de uma lista. Use foreach para ler todos os elementos em ordem; é mais curto e não tem índice para errar.

Como escrever um laço infinito em C#?

for (;;) { ... } ou while (true) { ... }. As três partes do cabeçalho de um for são opcionais, e uma condição ausente conta como verdadeira. Saia do laço com break ou return quando a condição de saída for atendida.

Por que recebo IndexOutOfRangeException no meu laço for?

A condição deixa o índice chegar ao tamanho: i <= array.Length visita array[array.Length], uma posição depois do último elemento. Os índices válidos vão de 0 a Length - 1, então a condição deve ser i < array.Length (ou i < list.Count para uma List, que lança ArgumentOutOfRangeException no lugar).

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR