Regla 0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for
Estructuras de control y flujo (0x10XX)
0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for¶
Enunciado normativo¶
La condición de un
forDEBE ser una única comprobación de cota o rango (por ejemploi < n). Si el corte del lazo requiere combinar condiciones lógicas con&&o||, DEBE usarse un lazowhile.
¿Por qué existe esta regla?¶
El problema¶
La fortaleza del for es que su encabezado se lee de un vistazo: dónde empieza,
hasta dónde llega y cómo avanza. Cuando la condición pasa a ser
i < n && !encontrado && limite > 0, ese vistazo ya no alcanza: el encabezado
esconde varias causas de corte y el lector debe evaluar cómo interactúan.
Peor aún, la condición compuesta mezcla dos naturalezas: una cota estructural
(i < n) y un estado dinámico (!encontrado). Separarlas ayuda a razonar:
el for recorre el rango y el while espera el evento.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Legibilidad | Varias condiciones de corte compiten en una sola línea. |
| Bug lógico | El cortocircuito de && puede ocultar un acceso fuera de rango si se reordena. |
| Mantenibilidad | Cambiar una condición obliga a tocar el encabezado y el cuerpo. |
| Duplicación | La condición dinámica se reescribe también dentro del cuerpo. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §6.8.5.3 admite cualquier expresión en la condición del for;
la restricción es de diseño. La cátedra la adopta para reforzar el uso
comunicativo de cada lazo (ver 0x1003h: Utilizá el lazo for para iteraciones con rango o contador definido y while para lazos controlados por condiciones lógicas) y para prevenir el off-by-one del
antipatrón asociado. Se complementa con 0x1004h: Las condiciones complejas deben simplificarse o comentarse y 0x1013h: Detector de expresiones booleanas complejas sin paréntesis aclaratorios.
Alcance y excepciones¶
for (size_t i = 0; i < n; i++)es el patrón conforme por excelencia.Un
forcon dos variables del mismo rango (i < n && j >= 0) debe pasar awhile.Excepción razonable: condiciones que combinan dos cotas inseparables del mismo recorrido pueden admitirse si se comentan; ante la duda, usá
while.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — búsqueda con triple condición¶
for (int i = 0; i < n && !encontrado && limite > 0; i++)
{
if (valores[i] == buscado)
{
encontrado = true;
}
limite--;
}Por qué falla: el encabezado mezcla una cota de arreglo con un estado y un
presupuesto. Si se reordena &&, un acceso a valores[i] puede quedar sin la
protección de i < n.
❌ Contraejemplo 2 — for con corte por valor leído¶
for (int n = leer(); n != 0; n = leer())
{
procesar(n);
}Por qué falla: no hay contador ni rango; el corte depende del dato. El for
obliga a duplicar la lectura en dos cláusulas y no comunica un recorrido.
✅ Ejemplo conforme 1 — for con cota simple¶
for (size_t i = 0; i < cantidad; i++)
{
total += valores[i];
}Una única comprobación de cota; el recorrido completo se entiende desde el encabezado.
✅ Ejemplo conforme 2 — while para el corte dinámico¶
size_t i = 0;
bool encontrado = false;
while (i < cantidad && !encontrado)
{
if (valores[i] == buscado)
{
encontrado = true;
}
i++;
}El while es el lazo natural para una condición lógica; el avance queda en el
cuerpo porque ahora sí es parte de la lógica, no de una cota fija.
⚠️ Casos límite¶
i < nconsize_t: cuidar quenno sea negativo al convertirse a sin signo (ver la ficha de antipatrón 0x1005h).Cota estricta:
i <= nsobre{ref}0x301Bh`**).breakcomo corte: si se usabreakpara salir, conviene exponer la causa en la condición (ver 0x1002h: Restringí el uso de break y continue; preferí lazos con bandera de control).
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Regla 0x100Dh: condición de for compuesta. |
gcc | gcc -Wall -Wextra | No la detecta; es una regla de diseño. |
| Revisión manual | — | Encabezado de for con && o ` |
Checklist de autocontrol¶
¿La condición de cada
fores una sola comparación de cota?Si hay varias causas de corte, ¿la convertí en
while?¿Usé
i < ny noi <= npara arreglos de tamañon?¿Los tipos de
iynevitan conversiones de signo?
Reglas relacionadas¶
0x1003h: Utilizá el lazo for para iteraciones con rango o contador definido y while para lazos controlados por condiciones lógicas — elige
forpara rangos ywhilepara condiciones lógicas.0x1004h: Las condiciones complejas deben simplificarse o comentarse — simplifica condiciones complejas con variables auxiliares.
0x1013h: Detector de expresiones booleanas complejas sin paréntesis aclaratorios — exige paréntesis al mezclar
&&y||.
Antipatrón: Posible error off-by-one en condición de parada de bucle¶
Síntoma en el código del estudiante¶
La condición del for usa <= sobre un arreglo, o compara contra la dimensión
en lugar de contra el último índice:
int arr[10];
for (int i = 0; i <= 10; i++)
{
arr[i] = 0;
}Diagnóstico¶
Mecanismo del defecto¶
Un arreglo de tamaño N declarado int arr[N] tiene índices válidos de 0 a
N - 1 (ISO/IEC 9899:2011 §6.5.2.1). La condición i <= N incluye i == N, que
es exactamente un acceso fuera del arreglo. El error clásico es confundir “la
dimensión” con “el último índice válido”.
Consecuencia observable¶
El programa escribe (o lee) un elemento más allá del final: corrompe la variable
contigua, falla con SIGSEGV o pasa desapercibido según el diseño de memoria.
En arreglos locales suele pisar otra variable; en el heap, los metadatos del
asignador. Es un comportamiento indefinido.
Fundamento en el estándar C11¶
ISO/IEC 9899:2011 §6.5.2.1 define la indexación E1[E2] como *((E1)+(E2)) y
§6.5.6 la aritmética de punteros. Acceder a arr[N] construye un puntero un
elemento más allá del final del objeto, lo que no está permitido. La cátedra
exige la cota estricta i < N y recomienda size_t para índices (regla
0x3010h).
Corrección idiomática¶
❌ Código con el antipatrón¶
int arr[10];
for (int i = 0; i <= 10; i++)
{
arr[i] = 0;
}✅ Código refactorizado¶
int arr[10];
for (size_t i = 0; i < 10; i++)
{
arr[i] = 0;
}La cota i < 10 recorre exactamente los índices 0..9. Usar size_t mantiene
el índice sin signo y permite compararlo contra sizeof arr / sizeof arr[0].
Alternativa robusta:
int arr[10];
size_t n = sizeof(arr) / sizeof(arr[0]);
for (size_t i = 0; i < n; i++)
{
arr[i] = 0;
}Errores típicos al compilar o ejecutar¶
En ejecución, con -fsanitize=address:
ERROR: AddressSanitizer: stack-buffer-overflow on address ... READ of size 4
#0 in main
Sin sanitizador, el programa "funciona" pero corrompe la memoria contigua;
el fallo aparece luego, lejos de la causa.Checklist de verificación¶
¿La condición usa
<y no<=sobre una dimensión?¿El índice máximo alcanzable es
N - 1?¿El tipo del índice es
size_tcuando recorre un arreglo?¿Probé con un arreglo de tamaño 1 y con acceso al último elemento?
Reglas relacionadas¶
0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for — norma este antipatrón: cota simple en el
for.0x3010h: Las variables que representan tamaños o índices de arreglos deben ser de tipo size_t — índices y tamaños con
size_t.0x300Ch: Verificá siempre los límites de los arreglos antes de acceder a sus elementos — verificar límites antes de acceder a un arreglo.
Antipatrón: Invocación a strlen() en condición de parada de bucle for¶
Síntoma en el código del estudiante¶
La condición del for llama a strlen (u otra función costosa) en cada
iteración:
for (size_t i = 0; i < strlen(s); i++)
{
procesar(s[i]);
}Diagnóstico¶
Mecanismo del defecto¶
strlen(s) recorre la cadena entera contando caracteres hasta el '\0'
(ISO/IEC 9899:2011 §7.24.6.3). Al estar en la condición, se evalúa una vez por
iteración; el costo total es n recorridos de n caracteres, es decir O(n²).
La función no se llama una vez sino n veces.
Consecuencia observable¶
El programa funciona pero se degrada de forma notable con cadenas largas. Para una cadena de 100 000 caracteres, el lazo hace ~5 000 millones de comparaciones en lugar de 100 000. En un ejercicio pequeño casi no se nota; en una entrega con volumen es un problema de rendimiento medible.
Fundamento en el estándar C11¶
ISO/IEC 9899:2011 §7.24.6.3 define strlen sin garantía de memoización: cada
llamada recorre la cadena. El estándar no prohíbe usarla en la condición, pero
la cátedra exige sacar la evaluación invariante del lazo. Se relaciona con
0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for y con la separación de la condición de las operaciones costosas.
Corrección idiomática¶
❌ Código con el antipatrón¶
for (size_t i = 0; i < strlen(s); i++)
{
procesar(s[i]);
}✅ Código refactorizado¶
size_t len = strlen(s);
for (size_t i = 0; i < len; i++)
{
procesar(s[i]);
}La longitud se calcula una vez y se guarda en len; la condición solo compara
dos enteros. El costo baja a O(n) y el código es más fácil de leer.
Errores típicos al compilar o ejecutar¶
No hay error de compilación ni advertencia.
Con cadenas cortas el tiempo es imperceptible. Con cadenas de 100000
caracteres, el bucle tarda cientos de milisegundos en lugar de microsegundos.
Un perfil con perf o gprof muestra strlen en el tope del consumo.Checklist de verificación¶
¿La condición del lazo llama a
strlenu otra función costosa?¿El valor es invariante durante el lazo?
¿Lo guardé en una variable antes del
for?¿El tipo de la variable es
size_t?
Reglas relacionadas¶
0x1012h: Prohibición de expresiones de asignación dentro de estructuras de control — norma este antipatrón: evitar operaciones embebidas en el control.
0x100Dh: Prohibición de condiciones de parada compuestas complejas en lazos for — la condición del
fordebe ser una cota simple.0x3010h: Las variables que representan tamaños o índices de arreglos deben ser de tipo size_t — tamaños e índices con
size_t.