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 0x3005h: Minimizá el uso de múltiples niveles de indirección (punteros a punteros)

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x3005h: Minimizá el uso de múltiples niveles de indirección (punteros a punteros)

Enunciado normativo

DEBE evitarse la indirección múltiple (**, ***) salvo cuando sea estrictamente necesaria para el contrato de la función. Cuando haga falta modificar el puntero del llamador, DEBE preferirse retornar el puntero como valor de retorno en lugar de recibir T **.

¿Por qué existe esta regla?

El problema

Cada nivel de indirección agrega un salto mental: para saber qué contiene p, el lector debe resolver *p, luego **p, y recordar en qué nivel se encuentra. En el modelo de memoria, cada * corresponde a una dirección almacenada en memoria; seguir la cadena exige desreferenciar dos o tres veces, y cada desreferencia puede ser inválida si el nivel intermedio es nulo o colgante.

Los punteros a punteros aparecen por dos motivos: modificar el puntero del llamador (parámetro de salida) o representar arreglos de cadenas (char **argv). El primero casi siempre puede reemplazarse por un valor de retorno más claro; el segundo es legítimo y tiene forma canónica.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
LegibilidadFirmas como int ***ptr exigen reconstruir la cadena de memoria.
Errores de nivelEs fácil desreferenciar una vez de más o de menos (*p vs **p).
RobustezCada nivel puede ser nulo; las validaciones se multiplican.

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

C11 no limita la cantidad de niveles, pero cada * adicional apunta a un objeto distinto. La cátedra adopta el criterio de 0x0001h: La claridad y prolijidad son de máxima importancia: si la firma necesita explicación para leerse, es demasiado compleja.

Alcance y excepciones

Aplica a parámetros y variables locales. No alcanza a los casos canónicos:

Aun en los casos permitidos, la documentación debe explicar quién es dueño de cada nivel (ver 0x3006h: Documentá la propiedad de los recursos al utilizar punteros).

Ejemplos exhaustivos

❌ Contraejemplo 1 — Tres niveles para devolver un arreglo

void obtener_datos(int ***ptr_datos, size_t *cantidad);

Por qué falla: el llamador debe declarar int **datos;, pasar &datos y luego recordar que *datos es el arreglo a liberar. La firma no dice si el tercer nivel está reservado ni quién lo libera. Un valor de retorno elimina uno de los niveles y vuelve el contrato evidente.

❌ Contraejemplo 2 — T ** sólo para “poder asignar”

void inicializar(nodo_t **nodo, int valor)
{
    *nodo = malloc(sizeof(**nodo));
    (*nodo)->dato = valor;
}

Por qué falla: se usa el parámetro de salida para devolver una única cosa. El doble nivel complica la validación (nodo == NULL, *nodo == NULL) y cada acceso necesita paréntesis por la precedencia de * y ->. Devolver nodo_t * es más simple y admite la verificación de 0x3001h: Siempre verificá la asignación exitosa de memoria dinámica.

✅ Ejemplo conforme 1 — Retornar el puntero

int *obtener_datos(size_t *cantidad);
void usar(void)
{
    size_t cantidad = 0;
    int *datos = obtener_datos(&cantidad);
    if (datos == NULL)
    {
        return;
    }
    free(datos);
}

Se usa un solo parámetro de salida (size_t *) y el valor de retorno para el arreglo. El cliente lee el contrato sin resolver niveles de indirección.

✅ Ejemplo conforme 2 — Caso legítimo de dos niveles

int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; i++)
    {
        printf("%s\n", argv[i]);
    }
    return 0;
}

char *argv[] es un arreglo de cadenas: cada elemento es un char * y el arreglo decae a char **. Es la forma canónica y no se reemplaza por nada. La regla se respeta porque el doble nivel es el modelo natural del dominio.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cDeclaraciones con *** o ** en parámetros no canónicos.
gcc / clanggcc -Wall -Wextra -std=c11 ...No detecta complejidad, sí tipos incompatibles en las desreferencias.
Revisión manualParámetro T ** cuya única salida es un puntero.

Checklist de autocontrol

Reglas relacionadas