Regla 0x2006h: Mantené el alcance de las variables al mínimo posible
Funciones, contratos y modularizacion (0x20XX)
0x2006h: Mantené el alcance de las variables al mínimo posible¶
Enunciado normativo¶
DEBE declararse cada variable en el bloque más pequeño que la contenga y lo más cerca posible de su primer uso. NO DEBE diferirse la declaración al inicio de la función si solo se usa dentro de un lazo o de una rama.
¿Por qué existe esta regla?¶
El problema¶
Una variable declarada al inicio está “viva” durante todo el cuerpo aunque solo se use en las últimas líneas: cualquier bloque intermedio puede leerla por error y el lector debe recordar en qué estado está. Cuanto más corta es la ventana entre declaración y uso, menos estado hay que retener. El alcance mínimo también evita el reciclaje accidental, germen de 0x2011h: Prohibición de asignaciones múltiples a una variable sin lectura intermedia (dead store).
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Bug silencioso | Un bloque intermedio la usa antes de su inicialización. |
| Mantenibilidad | No se sabe en qué momento el dato cobra sentido. |
| Warning | El compilador marca variables sin uso o sin inicializar. |
Fundamento en el estándar y en la cátedra¶
C11 define el alcance por el bloque donde se declara (§6.2.1): al cerrarlo, la variable deja de existir. Desde C99 se permite declarar en medio del bloque (§6.8.2), así que no hay razón técnica para amontonarlas. La cátedra lo exige porque achica el estado activo; es la versión local de 0x2004h: No se permite el uso de variables globales.
Alcance y excepciones¶
Aplica a variables locales, incluidas las de los lazos for, que deben
declararse en su cláusula de inicialización. Excepción: un struct que se
inicializa por partes a lo largo de la función puede declararse al comienzo si
todos los usos pertenecen a la misma idea y no hay bloques intermedios.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Declarar arriba y usar al final¶
int procesar(const int datos[], size_t n)
{
int suma = 0;
int maximo = datos[0];
int min; /* declarada y nunca usada */
for (size_t i = 0; i < n; i++) {
suma += datos[i];
}
/* ... 60 líneas ... */
for (size_t i = 0; i < n; i++) {
if (datos[i] > maximo) {
maximo = datos[i];
}
}
return maximo;
}min se declara y nunca se usa (0x8001h: Una aserción por cada función de prueba); el retraso entre
declaración y uso obliga a recordar variables que no participan.
❌ Contraejemplo 2 — Variable que sobrevive a su propósito¶
void informe(void)
{
double promedio = 0.0;
for (int i = 0; i < CANTIDAD; i++) {
promedio += leer_nota(i);
}
promedio /= CANTIDAD;
for (int j = 0; j < CANTIDAD; j++) {
imprimir_nota(j);
}
}promedio queda en alcance durante el segundo lazo, donde ya no se usa.
✅ Ejemplo conforme 1 — Alcance de lazo¶
int maximo(const int datos[], size_t n)
{
if (n == 0) {
return ERROR_ARREGLO_VACIO;
}
int max = datos[0];
for (size_t i = 1; i < n; i++) {
if (datos[i] > max) {
max = datos[i];
}
}
return max;
}i existe solo dentro del for y max nace pegado a su primer uso.
✅ Ejemplo conforme 2 — Alcance de bloque condicional¶
int emitir_factura(const struct pedido_t *p)
{
if (p->tipo == CONTADO) {
double descuento = p->total * 0.10;
return (int)(p->total - descuento);
}
if (p->tipo == CREDITO) {
double recargo = p->total * 0.05;
return (int)(p->total + recargo);
}
return (int)p->total;
}Cada auxiliar vive solo en la rama que la usa; el descuento no puede filtrarse al camino de crédito.
⚠️ Casos límite¶
Lazos
forconsize_t: el índice se declara en el propiofory su tipo debe sersize_t(0x3010h: Las variables que representan tamaños o índices de arreglos deben ser de tipo size_t).
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Declaración separada de su primer uso. |
gcc / clang | gcc -Wall -Wextra -Wunused-variable ... | Variable sin usar o que oculta otra. |
Checklist de autocontrol¶
¿Declaré cada variable dentro del bloque que la usa?
Reglas relacionadas¶
0x2004h: No se permite el uso de variables globales — prohibición de estado global; el alcance mínimo es su versión local.
0x2011h: Prohibición de asignaciones múltiples a una variable sin lectura intermedia (dead store) — la variable longeva termina en dead stores.
Antipatrón: Variable local declarada pero no utilizada¶
Síntoma en el código del estudiante¶
Variables declaradas al inicio de la función que nunca se leen, o que se
declaran y se inicializan “por las dudas”. Suelen aparecer junto a índices
declarados decenas de líneas antes de su for y a copias de parámetros que
nadie usa.
Diagnóstico¶
Mecanismo del defecto¶
El estudiante reserva el marco de pila de una variable, le asigna un valor y nunca la lee. El compilador puede eliminar el almacenamiento, pero el lector no: ve el identificador en la cabecera de la función y asume que participa del cálculo. La variable fantasma contamina el modelo mental del que lee, porque no hay forma de saber si su ausencia de uso es intencional o un olvido.
Cuando la variable se declara lejos de su uso, se suma el problema de alcance de 0x2006h: Mantené el alcance de las variables al mínimo posible: la vida útil no coincide con el propósito. Y si el estudiante la reutiliza para otra cosa más abajo, aparece el riesgo de 0x7001h: Siempre debés inicializar las variables a un valor conocido (valor residual).
Consecuencia observable¶
GCC emite -Wunused-variable para las que nunca se usan y
-Wunused-but-set-variable para las que solo se escriben. El resultado es
código que parece más complejo de lo que es y, en el peor caso, un cálculo que
el autor creía vigente y fue descartado.
Fundamento en el estándar C11¶
Una variable local con duración automática existe desde su declaración hasta el cierre del bloque (§6.2.4 y §6.2.1). La norma permite declararla sin usarla; el problema es de claridad (0x0001h: La claridad y prolijidad son de máxima importancia) y de esfuerzo de lectura. El estándar no obliga a consumir el objeto, pero la cátedra sí exige que todo identificador declare una intención cumplida.
Corrección idiomática¶
❌ Código con el antipatrón¶
int main(void)
{
int no_usada = 10;
int i;
int suma = 0;
for (i = 0; i < 3; i++) {
suma += i;
}
return 0;
}✅ Código refactorizado¶
int main(void)
{
int suma = 0;
for (int i = 0; i < 3; i++) {
suma += i;
}
return 0;
}no_usada desaparece y i nace dentro del for. El estado activo de la
función queda reducido a suma, que sí se usa.
Errores típicos al compilar o ejecutar¶
aviso: variable 'no_usada' definida pero no usada [-Wunused-variable]
aviso: variable 'i' fijada pero no usada [-Wunused-but-set-variable]
error: 'suma' no se usa [-Werror=unused-variable] (con -Werror)Checklist de verificación¶
¿Cada variable que declaré se lee al menos una vez?
¿Declaré los índices dentro de su lazo?
¿El compilador no marca variables sin uso?
¿Hay variables que sobreviven a su propósito original?
Reglas relacionadas¶
0x2006h: Mantené el alcance de las variables al mínimo posible — regla que norma este defecto: alcance mínimo.
0x7001h: Siempre debés inicializar las variables a un valor conocido — la variable que no se usa no necesita valor inicial.
0x2011h: Prohibición de asignaciones múltiples a una variable sin lectura intermedia (dead store) — una variable no usada suele terminar en dead store.
0x0001h: La claridad y prolijidad son de máxima importancia — cada identificador debe aportar claridad.