Regla 0x2001h: Las funciones deben usar cláusulas de guarda y retornos anticipados para reducir la anidación profunda
Funciones, contratos y modularizacion (0x20XX)
0x2001h: Las funciones deben usar cláusulas de guarda y retornos anticipados para reducir la anidación profunda¶
Enunciado normativo¶
DEBE validarse al inicio toda precondición o condición de error y abandonarse la función con un retorno anticipado. El camino feliz queda al primer nivel. Si se adquieren recursos, la liberación se centraliza al final.
¿Por qué existe esta regla?¶
El problema¶
Validar dentro de cada rama sepulta el cuerpo útil bajo casos excepcionales. La
guarda invierte el orden: descarta primero lo inválido y deja el camino normal
al ras. Cada guarda es una afirmación local verificable (“si es NULL, no sigo”).
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Bug silencioso | Un caso borde mal ubicado deja el else correspondiente desanidado. |
| Mantenibilidad | Agregar una validación obliga a reindentar medio cuerpo. |
| Testing | El camino de error es difícil de alcanzar sin simular toda la cadena. |
Fundamento en el estándar y en la cátedra¶
En C11 cada return es un salto válido en cualquier punto (§6.8.4 y §6.8.6.4);
no hay exigencia de punto único de salida. La cátedra adopta las guardas para
bajar la carga cognitiva y hacer explícitos los contratos de 0x2003h: Todas las funciones deben incluir documentación completa y estructurada.
Alcance y excepciones¶
Cubre la validación de parámetros (NULL, rangos, arreglos vacíos) y errores
inmediatos (fallo de fopen o malloc). No obliga a eliminar los if
anidados cuando la lógica realmente combina condiciones.
Excepción explícita: si la función reserva memoria o abre archivos, se prefiere un único bloque de limpieza al final, en tensión con 0x200Ch: Cada función debe tener a lo sumo un return.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — La pirámide de condiciones¶
int procesar(struct dato_t *d)
{
if (d != NULL) {
if (d->activo == true) {
if (d->valor > 0) {
return calcular(d);
}
}
}
return -1;
}La línea que importa está en el cuarto nivel y no se distingue la causa del error.
❌ Contraejemplo 2 — Guarda que fuga el recurso¶
FILE *abrir(const char *ruta)
{
if (ruta == NULL) {
return NULL;
}
FILE *f = fopen(ruta, "r");
if (f == NULL) {
return NULL;
}
if (fseek(f, 0, SEEK_END) != 0) {
return NULL;
}
return f;
}El retorno del fseek deja el archivo abierto: la guarda olvida la limpieza.
✅ Ejemplo conforme 1 — Guardas para el camino feliz¶
int procesar(const struct dato_t *d)
{
if (d == NULL) {
return ERROR_PUNTERO_NULO;
}
if (d->activo == false) {
return ERROR_DATO_INACTIVO;
}
if (d->valor <= 0) {
return ERROR_DATO_INVALIDO;
}
return calcular(d);
}Cada precondición se verifica de a una y el cuerpo útil queda al primer nivel.
✅ Ejemplo conforme 2 — Guardas y limpieza centralizada¶
int cargar(const char *ruta, struct tabla_t *t)
{
int resultado = ERROR_GENERICO;
if (ruta == NULL || t == NULL) {
return ERROR_PUNTERO_NULO;
}
FILE *f = fopen(ruta, "r");
if (f == NULL) {
return ERROR_APERTURA;
}
if (leer_tabla(f, t) == 0) {
resultado = OK;
}
if (fclose(f) != 0) {
resultado = ERROR_CIERRE;
}
return resultado;
}Las guardas previas a la apertura son seguras y el fclose se concentra en un punto.
⚠️ Casos límite¶
Guarda de una línea: igual necesita llaves por 0x1001h: Todas las estructuras de control deben utilizar llaves.
assertfrente a guarda:assertvalida invariantes de desarrollo; una entrada externa se valida con guarda.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Anidación superior a tres niveles. |
gcc / clang | gcc -Wall -Wextra -Wmisleading-indentation ... | else que parece pertenecer a otro if. |
Checklist de autocontrol¶
¿Validé las precondiciones al principio y no dentro del cuerpo?
¿El camino feliz quedó al primer nivel?
¿Si hay recursos, la liberación ocurre en un único punto?
Reglas relacionadas¶
0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única) — menos responsabilidades implican menos condiciones.
0x2013h: Detector de bloques else superfluos tras sentencias terminales — el
elsetras un retorno es el mismo problema sin guardas.