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 0x301Dh: Diseñá los Tipos de Datos Abstractos utilizando punteros opacos

Memoria, punteros y tipos (0x30XX)

Universidad Nacional de Río Negro

0x301Dh: Diseñá los Tipos de Datos Abstractos utilizando punteros opacos

Enunciado normativo

DEBE declararse cada Tipo de Dato Abstracto (TAD) como un tipo incompleto en la cabecera pública, y NO DEBE exponerse su representación física (el struct con sus campos) fuera del archivo .c. La cabecera sólo declara typedef struct <nombre> <nombre>_t; y las firmas de la interfaz; la definición completa vive en la implementación.

¿Por qué existe esta regla?

El problema

Un puntero opaco (opaque pointer) apunta a un tipo cuya representación el cliente desconoce. En C11 el nombre puede declararse sin definirlo (struct lista_t;) y completarse sólo en el .c; el cliente manipula lista_t * sin acceder a los campos. Si la cabecera los expone, cualquier archivo que la incluya puede leerlos y escribirlos, rompe el invariante interno y desincroniza el TAD.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Invariantes rotosEl cliente modifica cabeza, cantidad o capacidad directamente y desincroniza el TAD.
MantenibilidadCambiar un campo obliga a recompilar todos los clientes y a revisar cada acceso directo.
AcoplamientoLa lógica de la estructura se filtra por todo el programa.

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

C11 §6.2.5 permite declarar punteros a tipos incompletos; el tipo se completa dentro de la unidad de traducción que lo define. La cátedra adopta el puntero opaco porque fuerza a que toda la manipulación pase por las funciones de interfaz, únicas que pueden documentar y verificar el contrato.

Alcance y excepciones

Aplica a todo TAD con estado compartido: pilas, colas, listas, árboles, buffers. No aplica a estructuras de datos planas que se usan como registro de paso (struct punto_t { int x; int y; };), donde exponer campos es legítimo porque no hay invariante que proteger.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Cabecera que expone los campos internos

/* lista.h */
typedef struct lista_t
{
    struct nodo *cabeza;
    size_t cantidad;
} lista_t;

Por qué falla: cualquiera que incluya lista.h puede escribir lista->cantidad = 0; sin vaciar los nodos. El invariante “cantidad coincide con la longitud real” deja de estar garantizado por el módulo y pasa a depender de la disciplina de cada cliente.

❌ Contraejemplo 2 — Tipo opaco falso mediante void *

/* lista.h */
typedef void *lista_t;
lista_t lista_crear(void);
void lista_agregar(lista_t lista, int valor);

Por qué falla: void * oculta los campos, pero también borra la información de tipo. lista_agregar(otro_puntero, 3) compila sin diagnóstico y el error se manifiesta como corrupción de memoria en ejecución. El tipo incompleto struct lista_t conserva la verificación de tipos que void * pierde.

✅ Ejemplo conforme 1 — Cabecera con tipo incompleto

/* lista.h */
#ifndef LISTA_H
#define LISTA_H
#include <stddef.h>
typedef struct lista_t lista_t;
lista_t *lista_crear(void);
void lista_agregar(lista_t *lista, int valor);
size_t lista_cantidad(const lista_t *lista);
void lista_destruir(lista_t *lista);
#endif
/* lista.c */
#include "lista.h"
#include <stdlib.h>
struct lista_t
{
    struct nodo *cabeza;
    size_t cantidad;
};

El cliente compila contra la declaración incompleta; sólo el módulo conoce la representación. sizeof(lista_t) es un error de compilación en el cliente, lo cual es exactamente lo deseado.

✅ Ejemplo conforme 2 — Interacción con acceso de sólo lectura

size_t lista_cantidad(const lista_t *lista)
{
    if (lista == NULL)
    {
        return 0;
    }
    return lista->cantidad;
}

La función lista_cantidad recibe const lista_t *: el cliente no puede modificar el TAD y el módulo conserva la potestad sobre los campos. Combina la opacidad con el contrato de sólo lectura de 0x3007h: Los argumentos de tipo puntero deben ser const siempre que la función no los modifique.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cCampos de struct de TAD visibles en el .h.
gcc / clanggcc -Wall -Wextra -std=c11 -c cliente.csizeof(lista_t) o acceso a campos da error de tipo incompleto.

Checklist de autocontrol

Reglas relacionadas