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 0x3006h: Documentá la propiedad de los recursos al utilizar punteros

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3006h: Documentá la propiedad de los recursos al utilizar punteros

Enunciado normativo

Cuando una función recibe o devuelve un puntero a un recurso, su documentación DEBE indicar quién es el dueño del recurso y, por lo tanto, quién debe liberarlo. Si la función transfiere la propiedad, DEBE declararlo; si sólo la presta, también.

¿Por qué existe esta regla?

El problema

En C no existe un recolector de basura ni un sistema de tipos que codifique la propiedad de la memoria. El compilador ve un char * y no sabe si apunta a una cadena literal (no liberable), a memoria del llamador (prestada) o a un bloque recién reservado (transferido). Esa información sólo vive en la documentación.

Sin ella, dos errores simétricos son igual de probables: fugar memoria porque nadie libera, o liberar dos veces porque ambos lados creyeron ser dueños. El contrato debe decir explícitamente qué espera la función del llamador y qué entrega de vuelta.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Fuga de memoriaNadie libera porque cada parte asumió que la otra lo haría.
Doble liberaciónAmbos lados liberan el mismo bloque por no saber quién es dueño.
Uso de memoria prestadaEl llamador libera datos que pertenecen a otro módulo.
MantenibilidadEl código cambia cuando alguien descubre el contrato real por ensayo y error.

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

C11 no define la propiedad como concepto del lenguaje; por eso la cátedra la convierte en requisito documental de toda función con punteros, en el marco de 0x2003h: Todas las funciones deben incluir documentación completa y estructurada (documentación estructurada). Es la contraparte explícita de 0x3005h: Minimizá el uso de múltiples niveles de indirección (punteros a punteros): cuanto más niveles de indirección hay, más importante es decir quién responde por cada uno.

Alcance y excepciones

Aplica a toda función que reciba o devuelva T * o T ** hacia memoria dinámica, archivos, sockets u otros recursos. No aplica a punteros que apuntan a objetos del propio llamador sin transferencia de vida (por ejemplo, size_t * para un contador local), aunque documentar la intención nunca estorba. Las funciones static internas pueden tener documentación más breve si el contrato es evidente en el módulo, pero la anotación de propiedad no se omite.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Constructor sin contrato de propiedad

char *crear_cadena(const char *origen);

Por qué falla: el llamador no sabe si debe liberar el retorno con free, si la cadena es estática, ni si puede fallar. Distintas personas escribirán distinto código de limpieza y aparecerán fugas o dobles liberaciones. La firma sola no alcanza: hace falta documentar.

❌ Contraejemplo 2 — Función que libera memoria del llamador

void procesar(nodo_t *nodo)
{
    usar(nodo->dato);
    free(nodo);
}

Por qué falla: procesar recibe un puntero prestado y lo libera sin avisar. El llamador conserva su puntero y lo vuelve a liberar o lo usa: use-after-free y double free. El nombre no sugiere destrucción; el contrato está oculto en el cuerpo.

✅ Ejemplo conforme 1 — Documentar transferencia de propiedad

/**
 * @brief Reserva una copia de la cadena.
 * @param origen Cadena de sólo lectura; no debe ser NULL.
 * @return Puntero reservado con malloc. El LLAMADOR es dueño y
 *         debe liberarlo con free(); devuelve NULL si no hay memoria.
 */
char *cadena_copiar(const char *origen);

El bloque @return fija las tres preguntas: quién libera, con qué función, y qué significa NULL. El cliente no necesita leer la implementación.

✅ Ejemplo conforme 2 — Documentar préstamo sin transferencia

/**
 * @brief Recorre la lista e imprime sus elementos.
 * @param lista Lista de sólo lectura; el llamador conserva su propiedad
 *              y no debe liberarla por esta llamada.
 * @pre lista != NULL.
 * @post No se modifica el estado de la lista.
 */
void lista_imprimir(const lista_t *lista);

El @param aclara que la función no libera ni retiene el recurso, y el const de 0x3007h: Los argumentos de tipo puntero deben ser const siempre que la función no los modifique lo refuerza a nivel de tipos. Documentación y firma dicen lo mismo.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cFunción con T * sin bloque de documentación de propiedad.
gcc / clanggcc -Wall -Wextra -std=c11 ...No evalúa documentación.
Revisión manualLlamadores con free incierto o ausente sobre el retorno.

Checklist de autocontrol

Reglas relacionadas