El bucle que siempre se ejecuta una vez
Un bucle while comprueba antes de actuar, así que puede ejecutarse cero veces. Eso normalmente está bien. A veces está exactamente mal: no puedes saber si repetir una petición hasta que la hayas mostrado, y no puedes juzgar la entrada de un usuario hasta que hayas leído algo. do-while mueve la prueba al final, así que el cuerpo se ejecuta primero y la condición solo decide si hacerlo otra vez.
La condición i < 5 es falsa desde el principio. El bucle while se ejecuta cero veces; el do-while se ejecuta exactamente una. Esa es toda la diferencia entre ellos.
Sintaxis, y ese punto y coma
do {
// cuerpo - siempre se ejecuta al menos una vez
} while (condición);
El orden se lee tal como se ejecuta: haz el cuerpo, mientras la condición siga cumpliéndose, hazlo de nuevo.
El punto y coma final después del paréntesis de cierre es obligatorio. do ... while (cond); es una sola sentencia, y las sentencias de C terminan en punto y coma. Omítelo y el compilador sigue leyendo, y luego reporta un error de sintaxis en algún lugar más abajo, que es la razón por la que el error real suele estar varias líneas antes de la línea a la que apunta el mensaje.
Este también es el único lugar en C donde while (cond); con punto y coma es correcto en lugar de ser un error. En un bucle while normal la misma puntuación crea un cuerpo vacío y normalmente un bucle infinito; aquí está cerrando un do.
Las llaves son opcionales para un cuerpo de una sola sentencia pero, como con cualquier otra estructura de control de C, escríbelas igual.
El patrón de validación de entrada
Para esto fue diseñado do-while. Tienes que leer algo antes de poder decir si es aceptable, así que la lectura tiene que ocurrir antes de la prueba:
El bucle sigue leyendo hasta que un valor pasa, y la misma condición que rechaza un valor malo es la que pide otra pasada. Escrito como un while, la lectura tendría que aparecer dos veces — una antes del bucle para preparar la condición, otra al final del cuerpo — que es la duplicación que do-while elimina.
Con entrada real el cuerpo llamaría a scanf en su lugar:
int age;
do {
printf("Ingresa tu edad (1-120): ");
if (scanf("%d", &age) != 1) {
while (getchar() != '\n') { } /* descarta la línea mala */
age = -1; /* fuerza otra pasada */
}
} while (age < 1 || age > 120);
Fíjate en el while interno que vacía el resto de la línea. Si scanf no logra convertir, el texto ofensivo sigue sentado en el buffer de entrada y cada lectura posterior también falla con él: un bucle que da vueltas para siempre imprimiendo la petición. Limpiarlo es parte del patrón.
El patrón de menú
El otro uso clásico: mostrar las opciones, leer una elección, actuar sobre ella y repetir hasta que el usuario salga. El menú debe aparecer antes de que haya alguna elección que probar.
break dentro del switch termina el switch, no el bucle: un punto que vale la pena precisar, ya que ambos usan la misma palabra clave. Aquí el bucle termina porque choice != 4 se vuelve falso. Si sí quisieras salir del bucle desde dentro del switch, return o una variable bandera serían el camino; break por sí solo no puede hacerlo.
Una primera pasada garantizada puede ser un error
Como el cuerpo se ejecuta antes de cualquier prueba, do-while procesará alegremente datos que no existen:
/* ERROR: imprime a[0] incluso cuando n es 0 */
int i = 0;
do {
printf("%d\n", a[i]);
i++;
} while (i < n);
Con n == 0 esto lee a[0] en un array vacío: comportamiento indefinido. Cualquier bucle sobre una colección que pueda estar vacía quiere un while o un for, cuya prueba va primero.
La regla general: usa do-while solo cuando una ejecución de cero iteraciones sea imposible por la naturaleza del problema, no simplemente poco probable en la práctica. Pedirle algo a un usuario, tirar los dados hasta que salga un objetivo, generar y probar un candidato: esos genuinamente no se pueden juzgar antes de la primera pasada. Recorrer una lista no puede hacer esa afirmación.
break y continue en un do-while
Ambos funcionan, y continue tiene un giro que vale la pena conocer: salta a la prueba de la condición, no al principio del cuerpo.
El i++ al principio del cuerpo se ejecuta en cada pasada, incluidas las que continue corta, así que el bucle sigue avanzando. Pon la actualización después de un continue y el bucle se cuelga: la misma trampa que tienen los bucles while.
break sale del bucle de inmediato y se salta la condición por completo.
Elegir entre los tres bucles
Los tres bucles de C son igual de poderosos; la elección es sobre cuál declara mejor la intención.
for un contador, su límite y su paso van juntos - "n veces"
while repetir hasta que una condición cambie, y quizá ninguna vez - "hasta que"
do-while lo mismo, pero la primera pasada está garantizada - "al menos una vez"
En la práctica do-while es con mucho el más raro de los tres. La mayoría de los bucles o cuentan o necesitan manejar el caso vacío, y ambos quieren la prueba arriba. Recurre a do-while cuando te descubras duplicando una sentencia justo antes de un bucle while para preparar su condición: esa duplicación es la señal.
Preguntas frecuentes
¿Cómo se escribe un bucle do-while en C?
do { cuerpo } while (condición); — el cuerpo va primero, la prueba va después, y toda la sentencia termina con un punto y coma. El cuerpo siempre se ejecuta al menos una vez, y luego se repite mientras la condición sea distinta de cero.
¿Cuál es la diferencia entre while y do-while en C?
Dónde está la prueba. Un bucle while comprueba la condición antes de la primera pasada, así que puede ejecutarse cero veces. Un do-while comprueba después, así que siempre se ejecuta al menos una vez. Elige do-while solo cuando esa primera pasada garantizada sea genuinamente lo que el problema necesita.
¿Por qué un bucle do-while necesita un punto y coma al final?
Porque do ... while (cond); es una única sentencia, y como toda sentencia de C termina con un punto y coma. Omitirlo produce un error de sintaxis confuso que apunta a la línea siguiente, ya que el compilador sigue leyendo esperando el resto de la sentencia.
¿Cuándo debería usar un bucle do-while?
Cuando el cuerpo tiene que ejecutarse antes de que sea posible saber si repetir: mostrar un menú antes de leer una opción, volver a pedir hasta que la entrada sea válida, o generar un valor y después probarlo. Si la condición se puede comprobar de antemano, un bucle while lo dice de forma más clara.