Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regla 0x7001h: Siempre debés inicializar las variables a un valor conocido

Robustez y manejo de errores (0x70XX)

Universidad Nacional de Río Negro

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 consecuenciaEfecto concreto
Compilación-Wmaybe-uninitialized puede detectar algunos casos, no todos.
Comportamiento indefinidoLeer un valor indeterminado puede dar cualquier resultado (§6.2.4p5).
Bug silenciosoEl resultado varía entre corridas y no se reproduce de forma estable.
DepuraciónAgregar instrumentación altera el layout de la pila y “cura” el bug.
SeguridadDatos 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

Cómo detectarla

HerramientaComandoSeñal
gcc / clanggcc -Wall -Wextra -Wuninitialized -std=c11warning: 'x' is used uninitialized.
Valgrindvalgrind ./progConditional jump or move depends on uninitialised value.
gaffgaff check archivo.cDeclaraciones locales sin inicializador evidente.
Revisión manualToda declaración sin = que luego se lea antes de escribirse.

Checklist de autocontrol

Reglas relacionadas

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

Reglas relacionadas

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

Reglas relacionadas