Menu

Bucle for en C#: sintaxis, cuenta atrás, bucles anidados y trampas

Cómo funciona el bucle for en C#: el inicializador, la condición y el iterador, contar hacia atrás y con saltos, varias variables de bucle, recorrer arrays por índice, bucles anidados, for(;;), y los bugs de desfase por uno y de borrar mientras se itera.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un bucle for repite un bloque mientras una condición es verdadera, con la preparación y el paso escritos en la cabecera del bucle. Es el bucle que se usa para contar: un número fijo de repeticiones, o cada índice de un array.

Salida:

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

Cómo se ejecutan las tres partes

for (initializer; condition; iterator) se ejecuta en un orden fijo:

  1. El inicializador (int i = 1) se ejecuta una vez, antes que nada.
  2. Se comprueba la condición (i <= 5). Si es falsa, el bucle termina y el cuerpo no vuelve a ejecutarse.
  3. Se ejecuta el cuerpo.
  4. Se ejecuta el iterador (i++) y el control vuelve al paso 2.

Como la condición se comprueba antes de cada iteración, un bucle for puede ejecutarse cero veces: for (int i = 10; i < 5; i++) nunca entra en su cuerpo. El bucle de arriba se comporta como este bucle while:

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

Las dos formas se diferencian en un detalle: continue dentro del cuerpo de un for sigue ejecutando el iterador, mientras que en esta versión con while saltaría por encima de i++, así que el bucle volvería a probar el mismo i y podría quedarse girando para siempre. Una variable declarada en el inicializador solo existe dentro del bucle. Si necesitas su valor final después, declárala antes del bucle: int i; for (i = 0; ...; i++) { }.

Contar hacia atrás y avanzar a saltos

El iterador puede ser cualquier expresión. Decrementa para contar hacia atrás, suma más de uno para avanzar a saltos, o multiplica para una secuencia geométrica:

Salida:

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

Asegúrate de que el iterador avanza hacia que la condición sea falsa. for (int i = 0; i < 10; i--) se aleja de 10 y solo se detiene cuando i da la vuelta tras dos mil millones de iteraciones.

Recorrer un array por índice

Los arrays exponen Length, las listas exponen Count, y los índices válidos van de 0 a uno menos que ese valor. El índice te da algo que foreach no da: acceso a la posición, a los vecinos y la posibilidad de escribir de vuelta.

Salida:

#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

El último bucle empieza en 1 porque compara cada elemento con el anterior. La página de arrays explica cómo crear, ordenar y copiar los propios arrays.

Varias variables de bucle

El inicializador puede declarar varias variables del mismo tipo, y el iterador puede actualizar varias, separadas por comas. El uso clásico son dos índices que avanzan el uno hacia el otro:

Salida:

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

Las dos variables deben compartir un tipo en una sola declaración: int left = 0, right = 10 funciona, int i = 0, string s = "" no.

Bucles anidados

Un bucle dentro de otro recorre todo su rango una vez por cada iteración del bucle exterior. Las tablas y las cuadrículas son el caso típico:

Salida:

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

El cuerpo interior se ejecuta 16 veces (4 × 4). Anidar multiplica el trabajo rápidamente: dos bucles anidados de 10.000 elementos cada uno son 100 millones de iteraciones. Un break en el bucle interior solo sale del bucle interior; la página de break y continue muestra cómo salir de los dos.

Bucles infinitos: for(;;)

Las tres partes de la cabecera son opcionales. Sin condición, el bucle se ejecuta hasta que algo dentro de él sale:

Salida:

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

for (;;) y while (true) compilan a lo mismo; elige el que use tu código base.

Errores de desfase por uno

El bug más común de for es una condición que llega un paso más allá de la cuenta:

Salida:

Ada
Grace
Linus
IndexOutOfRangeException at index 3

Un array de 3 tiene los índices 0, 1 y 2, así que i < names.Length es la condición correcta. El bug simétrico es contar hacia atrás con i > 0, que nunca visita el índice 0. En una List<T>, el mismo error lanza ArgumentOutOfRangeException.

Eliminar elementos mientras se recorre

Eliminar de una List<T> dentro de un bucle hacia delante desplaza una posición hacia abajo todos los elementos posteriores, así que el bucle se salta el elemento que va justo después de cada eliminación:

Salida:

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

El bucle hacia delante dejó un 0: tras eliminar el índice 1, el segundo cero pasó al índice 1, pero i++ ya había avanzado al índice 2. Recorrer hacia atrás lo evita porque las eliminaciones solo desplazan elementos que ya has visitado. Como alternativa de una línea, stock.RemoveAll(x => x == 0) hace lo mismo. Un bucle foreach no permite eliminar en absoluto; lanza InvalidOperationException.

Contadores de coma flotante

Evita los contadores de bucle double que se comparan por igualdad. 0.1 no tiene una representación binaria exacta, así que sumarlo diez veces no produce exactamente 1.0:

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

Cuenta con un entero y calcula el valor a partir de él: for (int i = 0; i <= 10; i++) { double x = i / 10.0; }.

for frente a foreach

NecesidadUsa
Leer cada elemento en ordenforeach
El índice, o los vecinos (i - 1, i + 1)for
Asignar a un elemento de un arrayfor
Avanzar de 2 en 2, ir hacia atrásfor
Eliminar de una List mientras recorresfor, hacia atrás
Una colección sin indexador (HashSet, Dictionary, resultados de LINQ)foreach
Repetir algo N veces, sin colecciónfor

Con arrays, los dos bucles compilan a prácticamente el mismo código, así que elige por legibilidad, no por velocidad.

Preguntas frecuentes

¿Cuál es la sintaxis de un bucle for en C#?

for (initializer; condition; iterator) { body }, por ejemplo for (int i = 0; i < 5; i++) { ... }. El inicializador se ejecuta una vez, la condición se comprueba antes de cada iteración y el iterador se ejecuta después de cada iteración. El bucle termina la primera vez que la condición es falsa.

¿Cómo recorro un bucle hacia atrás en C#?

Empieza en el último índice y decrementa: for (int i = items.Length - 1; i >= 0; i--). Fíjate en >= 0, no > 0, o te saltarás el primer elemento. Recorrer hacia atrás es también la forma segura de eliminar elementos de una List mientras iteras.

¿Cuándo debo usar for en lugar de foreach en C#?

Usa for cuando necesitas el índice (para comparar vecinos, escribir de vuelta en un array o avanzar de más de uno en uno), cuando cuentas en lugar de recorrer una colección, o cuando eliminas elementos de una lista. Usa foreach para leer cada elemento en orden; es más corto y no tiene un índice con el que equivocarse.

¿Cómo escribo un bucle infinito en C#?

for (;;) { ... } o while (true) { ... }. Las tres partes de la cabecera de un for son opcionales, y una condición ausente cuenta como verdadera. Sal del bucle con break o return cuando se cumpla tu condición de salida.

¿Por qué obtengo IndexOutOfRangeException en mi bucle for?

La condición deja que el índice llegue a la longitud: i <= array.Length visita array[array.Length], una posición después del último elemento. Los índices válidos van de 0 a Length - 1, así que la condición debería ser i < array.Length (o i < list.Count en una List, que lanza ArgumentOutOfRangeException en su lugar).

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR