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 0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0

Enunciado normativo

Los punteros nulos DEBEN inicializarse y compararse mediante la macro NULL, y NO DEBEN compararse contra el literal entero 0 ni contra el carácter '\0'.

¿Por qué existe esta regla?

El problema

0 y NULL son compatibles para el compilador: NULL se expande, según la cabecera, a 0, 0L o ((void *)0). Sin embargo, la semántica para el lector es distinta. Un 0 en una comparación puede ser un entero legítimo, un índice o un contador; NULL sólo puede ser un puntero. Escribir NULL documenta la intención sin ambigüedad.

El caso de '\0' es más grave: su valor es 0, de modo que if (ptr == '\0') compila y compara el puntero contra el entero cero. Pero '\0' describe el terminador de una cadena, no un puntero nulo; la mezcla de tipos confunde cadenas con punteros y es una fuente de errores didácticos.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
AmbigüedadEl lector no distingue si 0 es un entero o un puntero nulo.
Confusión de tipos'\0' sugiere una cadena donde hay un puntero.
ConsistenciaConviven == 0, == NULL y == '\0' para la misma idea.
Portabilidad de estiloOtros entornos esperan NULL/nullptr; 0 no comunica.

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

C11 §7.19 define NULL en <stddef.h> como una constante de puntero nulo. Una constante de puntero nulo es un entero constante con valor 0 o una expresión con el cast a void *; comparar contra ella es válido pero no expresivo. La cátedra adopta NULL por coherencia semántica y lo distingue de '\0' y de 0 en 0x1005h: Reemplazá las condiciones ambiguas basadas en la ‘veracidad’ (truthiness) del tipo de dato, que exige comparaciones explícitas según el tipo.

Alcance y excepciones

Aplica a la inicialización, asignación, retorno y comparación de punteros. No aplica a enteros: int x = 0; es correcto y no debe cambiarse por NULL. Tampoco prohíbe '\0' para comparar caracteres: if (c == '\0') es la forma correcta para el terminador de una cadena. La regla es específica de punteros.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Puntero inicializado con 0

int *ptr = 0;
if (ptr == 0)
{
    printf("Sin datos\n");
}

Por qué falla: el código es válido, pero 0 no dice que ptr sea un puntero nulo. Un lector apurado puede interpretarlo como un índice o como “posición cero”. NULL elimina esa duda y coincide con la convención del catálogo.

❌ Contraejemplo 2 — Comparación con el terminador de cadena

char *nombre = buscar_nombre(id);
if (nombre == '\0')
{
    printf("No encontrado\n");
}

Por qué falla: '\0' es un int con valor 0, así que la comparación se interpreta como nombre == 0. Compila, pero mezcla el espacio de las cadenas con el de los punteros: para el lector, nombre parece un char. La forma correcta es nombre == NULL.

✅ Ejemplo conforme 1 — Inicialización y comparación con NULL

int *ptr = NULL;
if (ptr == NULL)
{
    printf("Sin datos\n");
}

NULL deja claro que ptr es un puntero y que la condición pregunta por su validez. El mismo criterio se aplica al retorno de una búsqueda y a los parámetros.

✅ Ejemplo conforme 2 — Convivencia de NULL y '\0' en su tipo

const char *texto = obtener_texto();
if (texto != NULL)
{
    size_t i = 0;
    while (texto[i] != '\0')
    {
        i++;
    }
}

El puntero se compara con NULL; el carácter, con '\0'. Cada valor se compara contra el centinela de su propio tipo, en línea con 0x1005h: Reemplazá las condiciones ambiguas basadas en la ‘veracidad’ (truthiness) del tipo de dato.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cPuntero comparado o inicializado con 0 o '\0'.
gcc / clanggcc -Wall -Wextra -std=c11 ...comparison of pointer with integer zero según el contexto.
Revisión manualUso de NULL mezclado con 0/'\0' para punteros.

Checklist de autocontrol

Reglas relacionadas

Antipatrón: Chequeo innecesario antes de free()

Síntoma en el código del estudiante

Cada liberación se envuelve en una condición que “protege” a free de recibir un puntero nulo:

if (ptr != NULL) {
    free(ptr);
}

El chequeo se repite en cada free, como si liberar un nulo fuera peligroso.

Diagnóstico

Mecanismo del defecto

El defecto no es de memoria sino de ruido y falsa premisa. La creencia de fondo es “free(NULL) explota”. El estándar dice lo contrario: si el argumento es un puntero nulo, free no realiza ninguna acción. Por lo tanto, el if no cambia el comportamiento del programa: agrega una rama que ya estaba cubierta.

Ese ruido tiene un costo real: oculta la verdadera pregunta de propiedad (¿este bloque sigue vivo?), sugiere que el autor no confía en el contrato de free y multiplica la superficie de revisión.

Consecuencia observable

Fundamento en el estándar C11

Corrección idiomática

❌ Código con el antipatrón
if (ptr != NULL) {
    free(ptr);
}
✅ Código refactorizado
free(ptr);
ptr = NULL;

La línea útil es la asignación a NULL; el if desaparece porque no protegía nada.

✅ Ejemplo conforme 2 — liberación de una lista
while (cabeza != NULL) {
    nodo_t *siguiente = cabeza->sig;
    free(cabeza);
    cabeza = siguiente;
}

Acá sí hay una pregunta legítima (cabeza != NULL) porque se está recorriendo la lista, no porque se tema liberar nulo.

⚠️ Casos límite

Errores típicos al compilar o ejecutar

# El chequeo no protege contra el error real:
$ ./programa
free(): double free detected in tcache 2
Aborted (core dumped)

# Este patrón no produce advertencia; es estilo, no diagnóstico de compilador.

Checklist de verificación

Reglas relacionadas

Antipatrón: Comparación sintáctica errónea de puntero con carácter nulo ‘\0’

Síntoma en el código del estudiante

Para detectar el fin de una cadena se compara el puntero con el carácter nulo:

if (str == '\0') {
    /* ... */
}

El estudiante quiso preguntar “¿la cadena está vacía?” o “¿llegué al final?”, pero comparó la dirección con el valor cero.

Diagnóstico

Mecanismo del defecto

'\0' es una constante de tipo int con valor 0. Al comparar un puntero contra ella, C convierte el 0 en la constante de puntero nulo, de modo que str == '\0' es equivalente a str == NULL. La expresión compila y no advierte nada, pero no mide el contenido de la cadena: solo pregunta si el puntero es nulo.

Para saber si el primer carácter es el terminador hay que desreferenciar: *str == '\0' o, con azúcar, str[0] == '\0'. Recién ahí se lee el objeto apuntado.

Una sutileza: '\0' es entero, no carácter. La comparación idiomática de caracteres es contra '\0', pero la de punteros es contra NULL; mezclar dominios es el error de fondo.

Consecuencia observable

Fundamento en el estándar C11

Corrección idiomática

❌ Código con el antipatrón
if (str == '\0') {
    return 0;
}
✅ Código refactorizado — primer carácter
if (str == NULL) {
    return -1;
}
if (*str == '\0') {
    return 0;
}
✅ Recorrido de cadena con la distinción correcta
while (*str != '\0') {
    procesar(*str);
    str++;
}

Acá *str es un char y se compara contra '\0', que es su contraparte natural.

⚠️ Casos límite

Errores típicos al compilar o ejecutar

$ gcc -Wall -Wextra -std=c11 programa.c
# sin advertencia: la comparación puntero/0 es legal

$ ./programa
# la rama de "cadena vacía" nunca se ejecuta y el flujo continúa
Segmentation fault (core dumped)

Checklist de verificación

Reglas relacionadas