Menu

Bucle while en C: sintaxis, ejemplos y errores comunes

Cómo el bucle while de C repite hasta que una condición cambia: la regla de la condición primero, los bucles con centinela, leer entrada hasta EOF, while(1) y cómo convertir entre for y while.

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

Repetir sin un conteo

Un bucle for es la herramienta correcta cuando sabes —o puedes calcular— cuántas pasadas necesitas. Mucho trabajo no viene con un conteo. Leer cada línea de un archivo hasta que se acabe. Seguir pidiendo una contraseña hasta que sea correcta. Dividir un número a la mitad hasta que llegue a 1. Para estos, la pregunta no es "cuántas veces" sino "hasta cuándo", y eso es exactamente lo que expresa while.

Nadie calculó "siete" de antemano. El bucle simplemente sigue mientras la condición se cumpla.

Sintaxis y la regla de la condición primero

while (condicion) {
    // cuerpo: se ejecuta mientras la condicion sea distinta de cero
}

La condición se comprueba antes de cada pasada, incluida la primera. Tres consecuencias:

  • Un bucle cuya condición empieza siendo falsa se ejecuta cero veces. while (0) { ... } nunca ejecuta el cuerpo.
  • Aquello que comprueba la condición debe estar inicializado antes del bucle, o estarás comprobando un valor basura.
  • Algo en el cuerpo debe acabar volviendo falsa la condición, o el bucle nunca termina.

Como con if, los paréntesis son obligatorios y no va punto y coma después de ellos. Ese punto y coma suelto es la versión en bucle del mismo error:

/* BUG: la sentencia vacia es el cuerpo, esto gira para siempre */
while (i < 10);
{
    printf("%d\n", i);
    i++;
}

i nunca se incrementa, así que la condición nunca cambia. gcc -Wall señala el cuerpo vacío.

La regla de veracidad de C se aplica aquí igual que en if/else: el bucle continúa mientras la expresión sea distinta de cero y se detiene cuando es cero.

La forma estándar: inicializar, comprobar, actualizar

Casi todo bucle while correcto tiene tres piezas repartidas en tres lugares:

Esas son las mismas tres partes que contiene un encabezado for, solo que no reunidas en una línea. Lo cual es la respuesta honesta a "¿for o while?": si las tres piezas hablan de un solo contador, un for las pone donde quien lee puede comprobarlas juntas. Si la actualización está dispersa por un cuerpo largo, o la condición va de otra cosa que no sea un conteo, while es la forma más clara.

Olvidar la pieza 3 es el error de los bucles while. Si un bucle se cuelga, lo primero que hay que comprobar es si el cuerpo cambia de verdad lo que la condición lee.

Bucles con centinela

Un centinela es un valor especial que significa "para". Suma números hasta que el usuario introduzca un 0:

El 99 que va después del 0 nunca se alcanza: el centinela terminó el bucle. Fíjate en que la cantidad de números nunca se conoce de antemano, que es precisamente por lo que esto es un while.

Las cadenas de C funcionan igual: terminan con un centinela '\0', así que recorrer una es un while de manual:

Leer entrada hasta EOF

El while más común del mundo real lee entrada hasta que no hay más. La regla es comprobar el valor de retorno de la función de entrada, no el valor que guardó.

getchar devuelve el siguiente carácter, o la constante EOF cuando la entrada se acaba:

Dos detalles que importan. c es un int porque EOF es un valor negativo que no cabe en un char en muchos sistemas; declararlo char puede hacer que la comparación nunca se cumpla. Y la asignación va dentro de la condición: (c = getchar()) != EOF lee un carácter, lo guarda y compara en un solo paso. Los paréntesis interiores son obligatorios, porque != se asocia más fuerte que =; sin ellos guardarías en c el resultado de la comparación. Este es uno de los pocos lugares donde una asignación dentro de una condición es idiomática y no un error.

La misma forma funciona con scanf, cuyo valor de retorno es la cantidad de elementos convertidos con éxito:

int n;
while (scanf("%d", &n) == 1) {
    /* n contiene un numero valido */
}

El bucle termina al final del archivo y con la primera entrada que no sea un número, que suele ser lo que quieres.

Lo que no deberías escribir es while (!feof(f)). feof reporta si una lectura ya falló, así que sigue siendo falso cuando estás sobre el último elemento, el cuerpo del bucle se ejecuta una vez más y la lectura falla, procesando datos rancios. Comprueba la lectura en sí.

while (1) y break

A veces la condición de salida solo se conoce a mitad del cuerpo: después de leer, pero antes de procesar. La respuesta idiomática es un bucle infinito a propósito con una salida explícita:

while (1) siempre es verdadero, así que la única forma de salir es break (o return, o exit). Verás for (;;) usado con el mismo propósito; compilan a lo mismo, y cuál prefiere un proyecto es una cuestión de estilo.

Usa esta forma cuando la prueba de salida pertenezca genuinamente a la mitad del cuerpo. Cuando la prueba encaja de forma natural arriba, ponla en la condición donde quien lee pueda verla: un while (1) cuyo único break es la primera sentencia no es más que una condición disfrazada.

Convertir entre for y while

Cualquier bucle for se puede escribir como un while y al revés. La correspondencia es mecánica:

/* for */                          /* while equivalente */
for (init; cond; update) {         init;
    cuerpo;                        while (cond) {
}                                      cuerpo;
                                       update;
                                   }

Así que estos dos imprimen lo mismo:

El único punto donde la equivalencia se filtra es continue. En un bucle for, continue salta a la actualización del encabezado, así que el contador avanza igualmente. En la versión while, continue salta directo de vuelta a la condición y se salta el i++ del final del cuerpo: un bucle infinito instantáneo.

/* BUG: continue se salta i++, asi que el bucle gira para siempre en el primer numero par */
int i = 0;
while (i < 10) {
    if (i % 2 == 0) {
        continue;
    }
    printf("%d ", i);
    i++;
}

Si un bucle while usa continue, asegúrate de que la actualización ocurre antes, o usa un for, donde el encabezado lo garantiza.

Trampas comunes

  • Nada actualiza la condición. El cuerpo debe cambiar lo que la condición lee. Si un programa se cuelga, mira aquí primero.
  • Un punto y coma después del encabezado. while (cond); convierte la sentencia vacía en el cuerpo.
  • Comprobar una variable que nunca se inicializó. La condición se ejecuta antes del cuerpo, así que el valor que lee tiene que existir ya.
  • char en lugar de int para getchar. EOF no cabe en un char; la comparación puede entonces no cumplirse nunca, o un byte legítimo puede confundirse con EOF.
  • while (!feof(f)). Itera una vez de más. Comprueba la lectura.
  • Suponer que el cuerpo se ejecuta al menos una vez. No lo hace. Cuando necesitas una pasada garantizada —imprimir un menú antes de comprobar la elección— eso es un bucle do-while.

Preguntas frecuentes

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

while (condicion) { cuerpo }. La condición se comprueba antes de cada pasada, así que el cuerpo se ejecuta solo mientras la condición sea distinta de cero, y nada en absoluto si empieza siendo falsa. Algo dentro del cuerpo debe acabar cambiando la condición, o el bucle nunca termina.

¿Cuál es la diferencia entre for y while en C?

Son igual de potentes: cualquier for se puede reescribir como un while. Usa for cuando un contador, su límite y su paso pertenecen juntos a un encabezado, y while cuando la condición de parada no es un conteo: leer hasta el final del archivo, reintentar hasta tener éxito o iterar hasta que una bandera cambie.

¿Cómo leo entrada hasta EOF en C?

Comprueba el valor de retorno de la función de entrada, no el valor que leyó: while (scanf("%d", &n) == 1) { ... } se detiene al final del archivo o en la primera entrada no numérica, y while ((c = getchar()) != EOF) { ... } lee carácter por carácter. Nunca iteres con while (!feof(f)): se ejecuta una vez de más pasado el final.

¿Por qué mi bucle while es infinito en C?

Normalmente porque nada en el cuerpo cambia el valor que comprueba la condición, o porque un punto y coma suelto tras el encabezado (while (i < 10);) convirtió la sentencia vacía en el cuerpo. Ambos compilan sin quejas, así que comprueba que el cuerpo de verdad actualice la variable de la condición.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR