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 0x3009h: Documentá explícitamente los casos en que una función puede retornar NULL

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3009h: Documentá explícitamente los casos en que una función puede retornar NULL

Enunciado normativo

Si una función que devuelve un puntero puede retornar NULL, DEBE documentarse cuándo ocurre y qué significa cada caso en su documentación de retorno. Si la función nunca retorna NULL, esa garantía DEBE declararse también.

¿Por qué existe esta regla?

El problema

Un valor de retorno NULL es ambiguo: puede significar “no se encontró”, “no hay memoria”, “argumento inválido” o “fin de recorrido”. Si la documentación no lo aclara, el llamador debe adivinarlo leyendo el cuerpo y cada persona decidirá distinto. La documentación es el único lugar donde se asocia cada NULL con su condición.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Comportamiento indefinidoEl llamador desreferencia un NULL que creía imposible.
Bug silencioso“No encontrado” y “sin memoria” se tratan igual y el error se oculta.
RobustezEs imposible manejar el caso de fallo porque no se sabe cuándo ocurre.

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

C11 §7.22.3 sienta el precedente: las funciones que reservan memoria documentan que devuelven un puntero nulo si fallan. La cátedra extiende esa disciplina a toda función que retorne punteros, dentro del marco de documentación de 0x2003h: Todas las funciones deben incluir documentación completa y estructurada. La regla es hermana de 0x3006h: Documentá la propiedad de los recursos al utilizar punteros: una describe la propiedad, la otra, la validez.

Alcance y excepciones

Aplica a funciones y a funciones de interfaz de TAD que devuelvan T *. No aplica a funciones que retornan punteros garantizados no nulos, aunque también deben decirlo (@return nunca es NULL). Tampoco a funciones que no devuelven punteros. En funciones static de uso interno, la documentación puede ser más breve, pero la condición de retorno nulo no se omite.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Búsqueda sin documentación de retorno

nodo_t *buscar(nodo_t *cabeza, int id);

Por qué falla: el llamador no sabe si NULL significa “no está en la lista” (un resultado normal) o “la lista está corrupta” (un error). Distintas personas escribirán distinto código de manejo, y probablemente nadie contemple el caso nulo.

❌ Contraejemplo 2 — Documentación que promete de más

/**
 * @return Puntero al nodo solicitado.
 */
nodo_t *buscar(nodo_t *cabeza, int id)
{
    while (cabeza != NULL)
    {
        if (cabeza->id == id)
        {
            return cabeza;
        }
        cabeza = cabeza->sig;
    }
    return NULL;
}

Por qué falla: la documentación afirma que siempre devuelve un puntero válido, pero la implementación retorna NULL cuando no encuentra el nodo. El contrato escrito contradice al real y el llamador que confíe en él desreferenciará un nulo.

✅ Ejemplo conforme 1 — Documentar la condición de NULL

/**
 * @brief Busca un nodo por identificador.
 * @param cabeza Inicio de la lista; puede ser NULL (lista vacía).
 * @param id Identificador buscado.
 * @return Puntero al nodo con ese id, o NULL si no existe o si la
 *         lista está vacía. El llamador no es dueño del nodo devuelto.
 */
nodo_t *buscar(nodo_t *cabeza, int id);

La documentación responde las tres preguntas: cuándo NULL, qué significa y quién es dueño. El llamador puede decidir sin leer el cuerpo, y la propiedad queda amarrada a 0x3006h: Documentá la propiedad de los recursos al utilizar punteros.

✅ Ejemplo conforme 2 — Distinguir causas con un parámetro de salida

/**
 * @brief Busca un nodo y distingue el motivo del fallo.
 * @param cabeza Lista de sólo lectura.
 * @param id Identificador buscado.
 * @param encontrado Salida: true si el nodo existe, false si no.
 * @return Puntero al nodo, o NULL si no existe o si falla la búsqueda.
 * @pre encontrado != NULL.
 */
nodo_t *buscar_detallado(const nodo_t *cabeza, int id, bool *encontrado);

Cuando “no encontrado” y “error” deben diferenciarse, se usa una salida explícita. La documentación declara esa salida y ambas condiciones, en lugar de sobrecargar el significado de NULL.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cRetorno NULL sin documentación asociada en el bloque de la función.
gcc / clanggcc -Wall -Wextra -std=c11 -fanalyzer ...possible null pointer dereference en el llamador.
Revisión manualLlamador que desreferencia el retorno sin consultar la documentación.

Checklist de autocontrol

Reglas relacionadas