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 0x5012h: Detección de inclusiones cíclicas entre archivos de cabecera

Compilacion, preprocesador y seguridad (0x50XX)

Universidad Nacional de Río Negro

0x5012h: Detección de inclusiones cíclicas entre archivos de cabecera

Enunciado normativo

NO DEBEN incluirse mutuamente dos cabeceras. Si dos tipos se necesitan recíprocamente, se usa una declaración adelantada (struct nodo;) o se extrae el tipo común a una tercera cabecera.

¿Por qué existe esta regla?

El problema

Cuando a.h incluye b.h y b.h incluye a.h, el preprocesador entra en un ciclo. La guarda lo corta, pero deja a una cabecera procesada a medias: a.h empieza, incluye b.h, y b.h vuelve a a.h cuyo macro ya está definido, así que no vuelve a entrar y sigue con declaraciones que aún no existían. El tipo de a.h no está disponible dentro de b.h y el compilador reporta «tipo incompleto» en un archivo que parece correcto. La guarda evita la recursión infinita, pero no resuelve la dependencia circular: sólo la esconde.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Compilaciónincomplete type / unknown type name en una de las dos cabeceras.
FragilidadEl resultado depende del orden de los #include del cliente.
MantenibilidadLos módulos quedan acoplados en ambas direcciones sin mapa claro.

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

ISO/IEC 9899:2011 §6.2.5 distingue los tipos completos de los incompletos y §6.7.2.3 permite declarar una struct sin definirla (struct nodo;), lo que habilita punteros a ella antes de tener el cuerpo. §6.10.2 describe la inclusión como copia textual, origen del ciclo. La cátedra exige romperlo con declaraciones adelantadas o un tipo compartido: no dependen del orden.

Alcance y excepciones

Aplica a ciclos directos (a.hb.h) e indirectos (a.hb.hc.ha.h). Excepción: un ciclo que sólo involucra punteros se resuelve con una declaración adelantada y no es una violación. Se prohíbe necesitar el tipo completo (por valor, para sizeof o para acceder a campos) de una cabecera que a su vez depende de la primera.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Ciclo directo

/* a.h */
#include "b.h"
struct a { struct b *puntero_b; };
/* b.h */
#include "a.h"
struct b { struct a *puntero_a; };

Por qué falla: al procesar a.h se incluye b.h, que vuelve a a.h; la guarda la saltea y b.h intenta usar struct a cuando todavía no fue definida. Según cuál se incluya primero, una de las dos estructuras queda incompleta.

❌ Contraejemplo 2 — Dependencia por valor

/* fecha.h */
#include "evento.h"
struct fecha { struct evento evento; };

/* evento.h */
#include "fecha.h"
struct evento { struct fecha fecha; };

Por qué falla: además del ciclo, cada estructura contiene a la otra por valor, algo imposible en C porque el tamaño sería infinito. El compilador no puede determinarlo y aborta.

✅ Ejemplo conforme 1 — Declaración adelantada

/* b.h */
#ifndef B_H
#define B_H

struct a;

struct b { struct a *puntero_a; };

#endif

struct a; declara el tipo incompleto y basta para guardar un puntero. Para acceder a los campos se incluye a.h en el .c, no en la cabecera.

✅ Ejemplo conforme 2 — Tipo común extraído

/* nodo.h */
#ifndef NODO_H
#define NODO_H

struct nodo { int dato; struct nodo *sig; };

#endif

lista.h y pila.h incluyen nodo.h y ninguno incluye al otro. El acoplamiento es en estrella hacia el tipo compartido, sin ciclos.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check *.hReporta 0x5012h al detectar el ciclo en el grafo de inclusiones.
gccgcc -H -E archivo.cSe repite la ruta de cabeceras y falta procesar una parte.

Checklist de autocontrol

Reglas relacionadas