Regla 0x1002h: Restringí el uso de break y continue; preferí lazos con bandera de control
Estructuras de control y flujo (0x10XX)
0x1002h: Restringí el uso de break y continue; preferí lazos con bandera de control¶
Enunciado normativo¶
NO DEBE usarse
continuepara controlar un lazo, en ningún caso. El uso debreakse admite excepcionalmente para salir de forma anticipada de un lazo cuando simplifica la lógica, pero se DEBE preferir una bandera booleana de control en la condición del lazo.
El break obligatorio del switch no está alcanzado por esta regla (ver
0x1008h: Toda instrucción switch debe incluir un caso default).
¿Por qué existe esta regla?¶
El problema¶
continue corta la iteración en un punto intermedio y salta al incremento. El
lector que sigue el cuerpo de arriba hacia abajo debe recordar que existe una
puerta de salida lateral; cualquier sentencia colocada después del continue
puede no ejecutarse. break, cuando se usa como único mecanismo de corte,
convierte la condición del lazo en una mentira: el encabezado dice i < n, pero
el lazo en realidad se detiene por otra causa. La bandera de control traslada
esa causa a la condición, donde el lector la ve junto con la cota.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Bug silencioso | Código después del continue no se ejecuta y la omisión pasa desapercibida. |
| Mantenibilidad | Agregar una sentencia al final del cuerpo puede quedar tras el continue. |
| Testing | Los caminos de corte quedan ocultos y no se cubren con casos de prueba. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §6.8.6.2 define continue como un salto al final del cuerpo,
y §6.8.6.3 al break como salida inmediata. Ambos son válidos para el
compilador, pero la cátedra prioriza el flujo estructurado de salida única de
Dijkstra. Se admite break sólo cuando elimina un anidamiento o banderas
redundantes, y se prohíbe continue por su salto lateral.
Alcance y excepciones¶
continue: prohibido siempre en lazos de la materia.break: permitido para salida anticipada cuando ya se encontró el resultado buscado o cuando evita añadir otra bandera.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — continue que oculta una validación¶
for (int i = 0; i < n; i++)
{
if (valores[i] < 0)
{
continue;
}
suma += valores[i];
}Por qué falla: cualquier estadística que se agregue al final del cuerpo queda
después del continue y omitirá los negativos sin que nada lo recuerde. La
condición del for no anuncia el filtro.
❌ Contraejemplo 2 — break como única salida de un while (1)¶
while (1)
{
int c = leer_sensor();
if (c == CODIGO_CORTE)
{
break;
}
procesar(c);
}Por qué falla: while (1) declara un lazo que nunca termina. La causa real de
corte está enterrada en el cuerpo; el lector no puede saber cuándo sale mirando
el encabezado. Si aparece un segundo motivo de corte, se suma otro break.
✅ Ejemplo conforme 1 — bandera de control¶
bool terminar = false;
while (!terminar)
{
int c = leer_sensor();
if (c == CODIGO_CORTE)
{
terminar = true;
}
else
{
procesar(c);
}
}La condición !terminar expone la causa de corte y la salida es única. Sumar
otro motivo se resuelve activando la misma bandera.
✅ Ejemplo conforme 2 — corte expuesto en la condición¶
bool encontrado = false;
for (size_t i = 0; i < n && !encontrado; i++)
{
if (valores[i] == buscado)
{
encontrado = true;
}
}Acá el corte se expone en la condición y el break ni siquiera hace falta. Si
se optara por un break tras encontrado = true, seguiría siendo una salida
anticipada justificada: se encontró el resultado y no hay trabajo pendiente.
⚠️ Casos límite¶
Filtro masivo: reescribí la condición del
foren lugar de usarcontinue.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Regla 0x1002h: uso de break/continue. |
| Búsqueda | grep -n "continue" archivo.c | Toda aparición es una violación. |
| Revisión manual | — | while (1) con break como única salida. |
Checklist de autocontrol¶
¿Usé
continueen algún lazo? Debe eliminarse siempre.¿El
breakque usé elimina un anidamiento real y está comentado?¿La condición del lazo expone todas las causas de corte?
Reglas relacionadas¶
0x1008h: Toda instrucción switch debe incluir un caso default — el
breakde cierre de cadacasedelswitches obligatorio.0x1003h: Utilizá el lazo for para iteraciones con rango o contador definido y while para lazos controlados por condiciones lógicas — ayuda a elegir
forowhilesegún el tipo de control.0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for — complementa: si el corte es compuesto, conviene un
while.
Antipatrón: Bucle infinito con salida condicionada exclusivamente por exit()¶
Síntoma en el código del estudiante¶
Un lazo while (1) termina el programa entero mediante exit() en lugar de
salir de forma estructurada:
while (1)
{
if (fin)
{
exit(0);
}
procesar();
}Diagnóstico¶
Mecanismo del defecto¶
exit() termina el proceso completo (ISO/IEC 9899:2011 §7.22.4.4): abandona el
lazo, la función y toda la cadena de llamadas. El control nunca vuelve al
llamador, con lo que se omite cualquier limpieza pendiente definida después del
lazo (liberar memoria, cerrar archivos, restaurar estado).
Consecuencia observable¶
El programa termina “correctamente” para el usuario, pero deja recursos sin liberar y evita que el código de limpieza se ejecute. En pruebas unitarias, la función no puede retornar y la prueba muere con el proceso; no se puede distinguir el éxito del fallo ni continuar con el siguiente caso.
Fundamento en el estándar C11¶
ISO/IEC 9899:2011 §7.22.4.4 define exit como terminación del programa que
registra las funciones de atexit y cierra los flujos, pero no ejecuta el
código de limpieza local. La cátedra exige que el corte se exprese en la
estructura del lazo (regla 0x1002h): una bandera de control o, si el propósito
es salir de la función, un return.
Corrección idiomática¶
❌ Código con el antipatrón¶
while (1)
{
if (fin)
{
exit(0);
}
procesar();
}✅ Código refactorizado¶
bool terminar = false;
while (!terminar)
{
if (fin)
{
terminar = true;
}
else
{
procesar();
}
}La bandera expone la condición de corte y el lazo sale por su encabezado. Si el
corte debe abandonar la función, se usa return y el llamador retoma el control.
Errores típicos al compilar o ejecutar¶
No hay error de compilación.
En ejecución, la memoria asignada en procesar() no se libera; un
detector como valgrind o -fsanitize=address reporta:
ERROR: LeakSanitizer: detected memory leaks
directly lost: ... bytes in ... blocks
En una suite de pruebas, la prueba que invoca esta función termina
el proceso y las pruebas siguientes no se ejecutan.Checklist de verificación¶
¿El lazo sale con
exit()en lugar de una condición?¿Hay recursos que deban liberarse antes de salir?
¿Puedo usar una bandera de control o un
return?¿El código posterior al lazo debe ejecutarse siempre?
Reglas relacionadas¶
0x1002h: Restringí el uso de break y continue; preferí lazos con bandera de control — norma este antipatrón: banderas de control en lugar de saltos.
0x2001h: Las funciones deben usar cláusulas de guarda y retornos anticipados para reducir la anidación profunda — retornos anticipados y limpieza centralizada.
0x3002h: Liberá siempre la memoria dinámica y asigná NULL al puntero para mitigar punteros colgantes — liberar la memoria dinámica y anular el puntero.