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 0x3018h: Inicialización idiomática de agregados con {0} en lugar de memset inmediato

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3018h: Inicialización idiomática de agregados con {0} en lugar de memset inmediato

Enunciado normativo

Las variables locales de tipo struct o arreglo que deban quedar en cero DEBEN inicializarse en su declaración con = {0}. NO DEBE declararse la variable sin inicializar y emitir a continuación un memset para llevarla a cero.

¿Por qué existe esta regla?

El problema

Declarar una variable y luego inicializarla con memset separa dos hechos que deberían ser uno: la variable existe, y existe sin un valor definido durante un instante. En ese intervalo, cualquier acceso accidental lee basura. Además, el memset puede escribirse mal (ver 0x3016h: Orden incorrecto o sospechoso de argumentos en llamadas a memset) o puede olvidarse por completo en un camino de error.

La inicialización en la declaración elimina el estado intermedio: no hay ningún punto del código donde la variable sea observable sin un valor conocido. Es más corta, más difícil de omitir y el compilador la entiende como parte de la declaración.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Estado transitorioEntre la declaración y el memset la variable contiene basura.
Bug silenciosoSi el memset se omite en una rama, los campos quedan sin inicializar.
RuidoDos sentencias donde alcanza una; más código que revisar.
Errores de argumentosEl memset mal escrito no inicializa y nadie lo nota.

Fundamento en el estándar y en la cátedra

C11 §6.7.9 permite inicializar agregados con una lista entre llaves; si se da menos inicializadores que elementos, el resto se inicializa a cero. Por eso {0} lleva todos los campos a cero. La cátedra prefiere esta forma sobre memset por claridad y por la regla de inicialización de 0x7001h: Siempre debés inicializar las variables a un valor conocido. El memset queda para rellenar con bytes distintos de cero o para regiones ya existentes.

Alcance y excepciones

Aplica a agregados locales (arreglos y structs) que deban quedar en cero. No aplica a memoria del heap: un bloque reservado con malloc no puede inicializarse en la declaración; allí se usa calloc o un memset posterior con el orden correcto. Tampoco prohíbe memset para rellenar con un patrón distinto de cero (0xFF, 'A').

Ejemplos exhaustivos

❌ Contraejemplo 1 — Declarar y limpiar con memset

struct punto_t punto;
memset(&punto, 0, sizeof(punto));
punto.x = 1.0;

Por qué falla: entre la declaración y el memset, punto contiene basura; si una refactorización mueve código entre esas líneas, aparece un bug por dato sin inicializar. La forma idiomática lo expresa en una sola línea.

❌ Contraejemplo 2 — Arreglo del que se olvida el memset

int acumuladores[10];
if (modo_especial)
{
    memset(acumuladores, 0, sizeof(acumuladores));
}
acumuladores[0] += valor;

Por qué falla: el memset está dentro de una rama condicional, de modo que en el camino normal los acumuladores quedan sin inicializar y la suma parte de basura. Inicializar en la declaración elimina la posibilidad de olvidar la rama.

✅ Ejemplo conforme 1 — Inicialización en la declaración

struct punto_t punto = {0};
int acumuladores[10] = {0};
punto.x = 1.0;
acumuladores[0] += valor;

Ambas variables tienen un valor conocido desde el instante en que existen. No hay sentencia separada que pueda omitirse y el compilador inicializa a cero todos los elementos no nombrados.

✅ Ejemplo conforme 2 — Memoria del heap con calloc

int *valores = calloc(cantidad, sizeof(*valores));
if (valores == NULL)
{
    return NULL;
}

Cuando la memoria viene del heap, calloc inicializa en cero y a la vez verifica el desbordamiento del producto. Si se usara malloc, correspondería memset(valores, 0, cantidad * sizeof(*valores)) con el orden canónico y la verificación de 0x3001h: Siempre verificá la asignación exitosa de memoria dinámica. {0} no puede aplicarse a memoria reservada dinámicamente.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cDeclaración de agregado local seguida de memset a cero.
gcc / clanggcc -Wall -Wextra -Wuninitialized -std=c11 ...may be used uninitialized si falta la inicialización.
Revisión manualmemset(&x, 0, sizeof(x)) inmediatamente después de declarar x.

Checklist de autocontrol

Reglas relacionadas