Regla 0x7001h: Siempre debés inicializar las variables a un valor conocido
Robustez y manejo de errores (0x70XX)
0x7001h: Siempre debés inicializar las variables a un valor conocido¶
Enunciado normativo¶
DEBE garantizarse que toda variable posea un valor conocido antes de ser leída como R-Value. NO DEBE confiarse en la inicialización implícita del sistema operativo ni del compilador.
Siempre que sea posible, la inicialización se escribe en la misma sentencia de declaración.
¿Por qué existe esta regla?¶
El problema¶
Las variables automáticas (locales) viven en la pila y no se inicializan a
cero. Cada llamada a función reutiliza el mismo espacio y deja los bytes que
haya escrito la llamada anterior. Por eso una variable sin inicializar puede
valer 0 en una corrida y -1234567 en la siguiente: su valor es
indeterminado (§6.7.9p10).
El sistema operativo limpia la memoria entre procesos, no dentro del mismo
proceso. Confundir ambas cosas produce errores que desaparecen “mágicamente”
al agregar un printf que cambia el layout de la pila.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Compilación | -Wmaybe-uninitialized puede detectar algunos casos, no todos. |
| Comportamiento indefinido | Leer un valor indeterminado puede dar cualquier resultado (§6.2.4p5). |
| Bug silencioso | El resultado varía entre corridas y no se reproduce de forma estable. |
| Depuración | Agregar instrumentación altera el layout de la pila y “cura” el bug. |
| Seguridad | Datos residuales de otra función pueden filtrarse a la salida. |
Fundamento en el estándar y en la cátedra¶
El estándar define el valor de un objeto automático no inicializado como indeterminado (§6.7.9p10) y reserva la inicialización a cero para objetos con duración estática (§6.7.9p10, §6.2.4p3). La cátedra adopta la inicialización explícita como hábito defensivo: cuesta una asignación y elimina toda una familia de fallos no reproducibles.
Alcance y excepciones¶
Aplica a variables locales, campos de struct y elementos de arreglo. No es necesario inicializar un parámetro por valor, porque el llamador ya le dio un valor, ni un puntero de salida que la función asigna antes de cualquier lectura. La aritmética de punteros solo se permite sobre memoria inicializada.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Acumulador sin punto de partida¶
int suma;
for (size_t i = 0; i < cantidad; i++)
{
suma += valores[i];
}Por qué falla: suma arranca con el residuo que dejó en la pila la función
anterior; el resultado cambia de una corrida a otra.
❌ Contraejemplo 2 — Struct parcialmente inicializado¶
struct punto_t p;
p.x = 3;
printf("(%d, %d)\n", p.x, p.y);Por qué falla: p.y nunca recibió valor y se imprime basura; conviene
inicializar el agregado completo con {0} y luego pisar los campos.
✅ Ejemplo conforme 1 — Inicialización en la declaración¶
int suma = 0;
double promedio = 0.0;
struct datos_t d = {0};
int vec[5] = {0};Justificación: cada objeto nace con un valor determinista y los agregados se ponen a cero de una sola vez, incluidos los campos que no se usan todavía.
✅ Ejemplo conforme 2 — Inicialización aunque el valor se asigne después¶
int elegido = 0;
bool hay_elegido = false;
for (size_t i = 0; i < cantidad; i++)
{
if (valores[i] > mejor)
{
elegido = (int)valores[i];
hay_elegido = true;
}
}
if (hay_elegido)
{
printf("Mayor: %d\n", elegido);
}Justificación: si el arreglo está vacío, elegido conserva un valor definido
y la bandera hay_elegido evita leer un resultado que nunca se calculó.
⚠️ Casos límite¶
Salida de
scanf:int x; scanf("%d", &x);es técnicamente correcto si la lectura tiene éxito, pero la cátedra recomiendaint x = 0;y verificar el retorno.Punteros de salida: una función que asigna
*outantes de leerlo no viola la regla, aunque el compilador no siempre pueda probarlo.restrictyconst: una variableconstdebe inicializarse en su declaración; ver 0x301Ch: Detector de variables locales no inicializadas con modificador const.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gcc / clang | gcc -Wall -Wextra -Wuninitialized -std=c11 | warning: 'x' is used uninitialized. |
| Valgrind | valgrind ./prog | Conditional jump or move depends on uninitialised value. |
gaff | gaff check archivo.c | Declaraciones locales sin inicializador evidente. |
| Revisión manual | — | Toda declaración sin = que luego se lea antes de escribirse. |
Checklist de autocontrol¶
¿Toda variable local nace con un valor explícito?
¿Los structs y arreglos se inicializan con
{0}?¿Revisé los caminos donde la variable podría no asignarse?
¿Verifiqué el retorno de
scanfy funciones de entrada?
Reglas relacionadas¶
0x301Ch: Detector de variables locales no inicializadas con modificador const — toda variable
constdebe inicializarse en su declaración.0x3018h: Inicialización idiomática de agregados con {0} en lugar de memset inmediato — inicialización idiomática de agregados con
{0}.0x0010h: Prohibición de llaves redundantes en inicialización de tipos escalares — para escalares, inicializar sin llaves redundantes.
0x0002h: Una declaración de variable por línea — una variable por línea permite inicializar cada una.
Antipatrón: Declaración de variable mezclada tras sentencias ejecutables¶
Síntoma en el código del estudiante¶
La declaración de una variable aparece después de una o varias sentencias ejecutables, a mitad del bloque, y muchas veces sin inicializador:
int a = 1;
a = a + 5;
int b = 2; /* declaración retrasada */
total = a + b;Se reconoce a simple vista porque el int/char/double de b no está
alineado con el inicio del bloque, sino intercalado entre printf,
asignaciones o llamadas a función. El inventario de variables queda disperso y
la variable retrasada suele llegar con valor indeterminado.
Diagnóstico¶
Mecanismo del defecto¶
Desde C99 las declaraciones pueden convivir con sentencias dentro del mismo
bloque, y C11 §6.8.2 modela el bloque como una lista que admite ambos. Por eso
el código compila en modo -std=c11. El defecto tiene dos caras. La primera es
de lectura: con declaraciones repartidas, nadie ve en un solo lugar qué
variables existen ni cuáles están inicializadas. La segunda es de estado: si la
declaración retrasada no trae inicializador, la variable conserva un valor
indeterminado hasta que se le asigne algo; una lectura anterior toma memoria
basura (§6.7.9p10). Agrupar las declaraciones al inicio obliga a decidir el
valor de cada una antes de usarla.
Consecuencia observable¶
Con -Wdeclaration-after-statement el compilador advierte por la mezcla; con
-Wall -Wextra aparece además 'b' is used uninitialized. Si la variable se
lee sin asignar, el resultado depende de lo que hubiera en la pila y cambia
entre ejecuciones o entre compiladores.
Fundamento en el estándar C11¶
C11 §6.8.2 permite declaraciones entre sentencias, de modo que el estilo no es un error de traducción. En cambio, §6.7.9p10 establece que un objeto de duración automática sin inicializador explícito tiene valor indeterminado, y leerlo es comportamiento indefinido. La cátedra adopta la agrupación al inicio del bloque como convención de claridad y como recordatorio del contrato de inicialización de 0x7001h: Siempre debés inicializar las variables a un valor conocido.
Corrección idiomática¶
❌ Código con el antipatrón¶
int a = 1;
a = a + 5;
int b = 2;
total = a + b;✅ Código refactorizado¶
int a = 1;
int b = 2;
int total = 0;
a = a + 5;
total = a + b;Por qué mejora: todas las declaraciones quedan agrupadas al inicio del bloque, cada una con su valor inicial, y la lógica de asignación se separa de la declaración. El lector ve el conjunto de variables y sus valores de partida de un solo golpe.
Errores típicos al compilar o ejecutar¶
warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
warning: 'b' is used uninitialized [-Wuninitialized]Checklist de verificación¶
Todas las declaraciones del bloque preceden a la primera sentencia.
Cada variable se inicializa al declararse (0x7001h: Siempre debés inicializar las variables a un valor conocido).
Compilé con
-Wdeclaration-after-statementy sin advertencias.Declaré una variable por línea (0x0002h: Una declaración de variable por línea).
El nombre de cada variable describe su rol (0x0101h: Los identificadores deben ser descriptivos).
Reglas relacionadas¶
0x7001h: Siempre debés inicializar las variables a un valor conocido — inicializar toda variable a un valor conocido.
0x0002h: Una declaración de variable por línea — una declaración de variable por línea.
0x2006h: Mantené el alcance de las variables al mínimo posible — mantener el alcance de las variables al mínimo.
0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas — variables locales en
snake_case.0x0101h: Los identificadores deben ser descriptivos — identificadores descriptivos.
Antipatrón: Lectura de variable local no inicializada¶
Síntoma en el código del estudiante¶
La variable se lee antes de recibir un valor, típicamente en un operador compuesto:
int acumulador;
acumulador += valor;La señal es una declaración sin = seguida de una operación que usa la
variable a la derecha: +=, -=, ++, printf o una comparación. A
diferencia de 0x7001h, la declaración sí está al inicio del bloque; el
defecto es que nunca se le dio un valor conocido.
Diagnóstico¶
Mecanismo del defecto¶
acumulador += valor; equivale a acumulador = acumulador + valor;: el
acumulador de la derecha se lee primero. Como la variable es automática, vive
en la pila y no se inicializa a cero; conserva el valor que dejó el uso previo
de esa dirección de memoria. La lectura de un objeto automático sin
inicializador explícito es un valor indeterminado (C11 §6.7.9p10), y operar con
él puede dar comportamiento indefinido. La suma arranca de una base arbitraria
en vez de cero.
Consecuencia observable¶
gcc -Wall -Wextra emite warning: 'acumulador' is used uninitialized cuando
puede seguir el flujo. Si no lo detecta, la suma da un resultado distinto en
cada ejecución, porque depende del contenido residual de la pila. En programas
con esa base aleatoria, el bug es intermitente y difícil de reproducir.
Fundamento en el estándar C11¶
C11 §6.7.9p10 dispone que un objeto de duración automática sin inicializador explícito tiene valor indeterminado. §6.2.4 describe la duración de almacenamiento automática y §6.3.1 las conversiones que no pueden aplicarse a valores indeterminados sin riesgo. La cátedra lo prohibe porque todo cálculo debe partir de un valor conocido, tal como exige 0x7001h: Siempre debés inicializar las variables a un valor conocido.
Corrección idiomática¶
❌ Código con el antipatrón¶
int acumulador;
acumulador += valor;✅ Código refactorizado¶
int acumulador = 0;
acumulador += valor;Por qué mejora: el inicializador fija la base del acumulador en cero y elimina la dependencia del contenido residual de la pila. El operador compuesto ahora lee un valor conocido.
Errores típicos al compilar o ejecutar¶
warning: 'acumulador' is used uninitialized [-Wuninitialized]
warning: 'acumulador' may be used uninitialized [-Wmaybe-uninitialized]Checklist de verificación¶
Ninguna variable se lee antes de recibir un valor conocido.
Los acumuladores y contadores empiezan en
0.Los punteros empiezan en
NULL(0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0).Composiciones como
+=operan sobre un valor ya inicializado.Compilé con
-Wall -Wextray sin advertencias deuninitialized.
Reglas relacionadas¶
0x7001h: Siempre debés inicializar las variables a un valor conocido — inicializar toda variable a un valor conocido.
0x0003h: Un espacio antes y después de cada operador binario — espaciado de los operadores binarios y compuestos.
0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0 — inicializar y comparar punteros con
NULL.0x2006h: Mantené el alcance de las variables al mínimo posible — mantener el alcance de las variables al mínimo.
0x301Ah: Validador de uso idiomático de tipos booleanos estándar — uso idiomático de
bool,trueyfalse.