Menu

Bucle for en C: sintaxis, ejemplos y errores comunes

Cómo repetir código con el bucle for de C: el encabezado de tres partes, contar hacia arriba y hacia abajo, recorrer arrays con el truco de sizeof, bucles anidados, bucles infinitos y los errores de desplazamiento por uno y de tipos sin signo que atrapan a todos.

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

Por qué un bucle for

if y switch eligen una rama y la ejecutan una vez. Los programas reales necesitan hacer cosas repetidamente: imprimir cada puntuación, sumar una lista de números, dibujar diez filas de una cuadrícula, probar cada carácter de una cadena. El bucle for es el caballo de batalla de C para repetir código un número conocido de veces, con un contador que tú controlas.

Todo lo que un bucle for necesita cabe en un encabezado compacto, así que "cuántas veces y cómo" queda visible en una sola línea.

El encabezado de tres partes

Un encabezado for tiene tres partes separadas por punto y coma: un inicializador, una condición y una actualización.

for (inicializador; condicion; actualizacion) {
    // cuerpo: se ejecuta mientras la condición sea distinta de cero
}

Se ejecutan en un orden concreto, y conocerlo explica cualquier bucle for que llegues a leer:

  1. El inicializador se ejecuta una vez, antes que todo lo demás.
  2. Se comprueba la condición. Si es cero, el bucle termina de inmediato.
  3. Se ejecuta el cuerpo.
  4. Se ejecuta la actualización.
  5. Vuelta al paso 2.

Sigue el bucle de arriba con esa lista: int i = 0 se ejecuta una vez. 0 < 5 se cumple, así que el cuerpo imprime i = 0 y luego i++ deja i en 1. Se vuelve a comprobar la condición, y así sucesivamente. Cuando i llega a 5 la prueba falla, el cuerpo se omite y se imprime listo. El cuerpo se ejecutó exactamente cinco veces, con i tomando los valores de 0 a 4.

De ahí salen dos consecuencias directas. La condición se comprueba antes de la primera pasada, así que un bucle cuya condición empieza siendo falsa se ejecuta cero veces: for (int i = 10; i < 5; i++) nunca entra al cuerpo. Y la actualización se ejecuta después del cuerpo, así que i conserva el valor anterior durante todo el cuerpo.

Declarar el contador dentro del encabezado (int i = 0) es legal desde C99 y es la opción por defecto correcta: i existe entonces solo dentro del bucle, así que no puede chocar con nada posterior, y cada bucle de una función puede reutilizar el nombre sin problemas.

Contar hacia arriba, hacia abajo y a pasos

La actualización no está limitada a i++. Cuenta hacia atrás, avanza de a cualquier cantidad o duplica en cada pasada:

Haz que la condición combine con la actualización. Contar hacia arriba va con < o <=; contar hacia abajo va con > o >=. Equivocarse en esa combinación es la manera de escribir un bucle que nunca termina.

Fíjate en que el segundo bucle usa <= 10 porque 10 es un valor que queremos incluir, mientras que el primer ejemplo usó < 5 porque 5 no lo es. Esa elección es donde nacen los errores de desplazamiento por uno, así que hazla conscientemente: i < n se ejecuta n veces empezando en 0; i <= n se ejecuta n + 1 veces.

Recorrer un array

El uso más común de un bucle de conteo es recorrer un array por índice. El contador sirve a la vez como la posición que lees.

Hay dos cosas que llevarse de este ejemplo.

El truco de sizeof. sizeof(scores) es el tamaño del array completo en bytes, y sizeof(scores[0]) es el tamaño de un elemento, así que su cociente es la cantidad de elementos. Se adapta solo cuando agregas un elemento, cosa que un 5 escrito a mano no hace. La trampa —y es grande— es que solo funciona donde el array mismo está en ámbito. Pasa el array a una función y decae a un puntero, así que ahí sizeof da el tamaño de un puntero. Dentro de una función, toma siempre la longitud como parámetro aparte.

La condición es i < n, nunca i <= n. Un array de cinco elementos tiene índices válidos de 0 a 4. Leer scores[5] es comportamiento indefinido: podría imprimir basura, reventar o parecer que funciona mientras corrompe otra cosa en silencio. C no hace ninguna comprobación de límites, así que esto corre por tu cuenta.

Recorrer un array hacia atrás es la misma idea al revés:

Empieza en n - 1 (el último índice válido) y detente en 0 inclusive, de ahí >= y no >. Mantén i como un int con signo aquí; la siguiente sección explica por qué.

Bucles anidados

Pon un for dentro de otro para trabajar con cuadrículas, tablas o cada par de elementos. El bucle interno se ejecuta completo por cada paso del bucle externo.

El bucle externo fija una row; el interno recorre cada col de esa fila; el printf("\n") después del bucle interno cierra la línea. Mueve ese salto de línea dentro del bucle interno y toda la tabla se vuelve una sola columna: vale la pena probarlo, porque hace concreta la estructura del anidamiento.

Dale nombres distintos a los contadores. row/col o i/j están bien; reutilizar i para ambos oculta el contador externo y produce resultados desconcertantes. Y vigila el costo: un bucle de n dentro de otro de n ejecuta el cuerpo n * n veces, así que un par de bucles de 1.000 iteraciones son un millón de pasadas.

Aquí hay un bucle anidado haciendo trabajo real: un triángulo de asteriscos, donde el límite del bucle interno depende del contador externo.

break y continue

Dos palabras clave cambian el flujo a mitad del bucle. break sale del bucle de inmediato; continue salta el resto de la pasada actual y va directo a la actualización.

El primer bucle se detiene en el momento en que encuentra 7 y nunca comprueba los 93 valores restantes. El segundo usa continue para saltarse la impresión de los números pares; el i++ del encabezado sigue ejecutándose, así que el bucle continúa avanzando. Hay mucho más sobre ambos en break y continue, incluida la forma de escapar de un bucle anidado, algo que un solo break no puede hacer.

Bucles infinitos y partes vacías

Las tres partes del encabezado son opcionales. Omite la condición y se toma como permanentemente verdadera:

for (;;) {
    /* se ejecuta para siempre: sal con break o return */
}

for (;;) es el bucle infinito idiomático de C, y los punto y coma siguen siendo obligatorios. No es un error mientras algo dentro pueda hacer break, return o exit: los bucles de eventos y los de "seguir preguntando hasta que la entrada sea válida" se escriben exactamente así.

También se pueden omitir otras partes. Si el contador ya existe, sáltate el inicializador; si el cuerpo lo avanza, sáltate la actualización:

int i = 0;
for (; i < n; ) {
    /* ... */
    i += step;
}

Eso es legal, pero se lee peor que el bucle while que en realidad es. Prefiere un for cuando el contador, su límite y su paso pertenecen juntos al encabezado, y un while cuando no.

El operador coma en un encabezado

El inicializador y la actualización pueden contener varias expresiones separadas por comas, que es la forma de llevar dos contadores a la vez:

int i = 0, j = len - 1 declara ambos contadores, y i++, j-- avanza los dos. Este es el operador coma, que evalúa sus operandos en orden. Úsalo para contadores genuinamente paralelos; meter trabajo no relacionado en un encabezado solo lo esconde.

Trampas comunes

Un puñado de trampas explica la mayoría de los errores con bucles for en C.

Desplazamiento por uno. i <= n sobre un array de base cero lee un elemento más allá del final. Usa i < n para "n veces empezando en 0".

Un punto y coma después del encabezado. Esto compila y casi nunca es lo que querías decir:

/* BUG: el cuerpo del bucle es la sentencia vacia; el printf se ejecuta una vez */
for (int i = 0; i < 5; i++);
{
    printf("%d\n", i);
}

El ; se convierte en el cuerpo, así que el bucle gira cinco veces sin hacer nada y las llaves de abajo se ejecutan una vez como un bloque cualquiera. gcc -Wall avisa de esto.

Contadores sin signo contando hacia atrás. Esto es un bucle infinito:

/* BUG: un valor sin signo nunca es < 0 */
for (size_t i = n - 1; i >= 0; i--) {
    process(a[i]);
}

i >= 0 siempre es verdadero para un tipo sin signo. Cuando i llega a 0, i-- da la vuelta a un número positivo enorme y el bucle sigue adentrándose en memoria que no le pertenece. Usa un int con signo para las cuentas regresivas, o escribe la condición como for (size_t i = n; i-- > 0; ), que decrementa y comprueba en un solo paso y se detiene correctamente en cero.

Modificar el contador en dos lugares. Cambiar i dentro del cuerpo además de en el encabezado vuelve impredecible la cantidad de iteraciones. Elige un solo lugar.

Contadores de punto flotante. for (double x = 0.0; x != 1.0; x += 0.1) puede no terminar nunca, porque 0.1 no tiene representación binaria exacta y la suma pasa de largo 1.0 sin tocarlo. Itera con un conteo entero y calcula el valor dentro:

for (int i = 0; i <= 10; i++) {
    double x = i / 10.0;
    /* ... */
}

Cambiar la longitud de un array mientras lo recorres. La condición i < n vuelve a leer n en cada pasada, así que si el cuerpo encoge la colección, ajusta el índice deliberadamente en lugar de dejar que ambos se desincronicen.

Preguntas frecuentes

¿Cómo se escribe un bucle for en C?

Pon tres partes en el encabezado separadas por punto y coma: un inicializador, una condición y una actualización: for (int i = 0; i < 5; i++) { printf("%d\n", i); }. Eso ejecuta el cuerpo cinco veces con i tomando los valores de 0 a 4, y se detiene en cuanto la condición es falsa.

¿Cómo recorro un array en C?

Cuenta un índice desde 0 hasta la longitud sin incluirla: for (int i = 0; i < n; i++) { sum += a[i]; }. Para un array declarado en la misma función puedes calcular la longitud con sizeof(a) / sizeof(a[0]); una vez que el array se pasa a una función ese truco deja de funcionar, así que pasa la longitud como parámetro.

¿Por qué mi bucle for en C se ejecuta una vez de más?

Ese es el error de desplazamiento por uno (off-by-one). Con un array de base cero de tamaño n, los índices válidos van de 0 a n - 1, así que i <= n ejecuta una iteración extra y lee más allá del final: comportamiento indefinido. El valor por defecto seguro es i < n.

¿Se puede declarar la variable del bucle dentro del for en C?

Sí, desde C99: for (int i = 0; i < n; i++). La variable existe entonces solo dentro del bucle, que es lo que quieres. Compila con gcc -std=c99 o posterior (GCC y clang modernos usan C17 por defecto, así que funciona sin más); el viejo estilo C89 declaraba i antes del bucle.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR