only modo loop initial for error declarations are allowed c include-guards

modo - C file file loops



c99 mode in c (10)

Creo que el problema aquí no es la falta de incluir guardia, sino el hecho de que las dos estructuras se necesitan mutuamente en su definición. Entonces, es un tipo que define el problema del huevo y de los huevos.

La forma de resolverlos en C o C ++ es hacer declaraciones directas sobre el tipo. Si le dice al compilador que ese elemento es una estructura de algún tipo, el compilador puede generar un puntero a él.

P.ej

Dentro de tree.h:

// tell the compiler that element is a structure typedef: typedef struct element_ element; typedef struct tree_ tree; struct tree_ { tree *first_child; tree *next_sibling; int tag; // now you can declare pointers to the structure. element *obj; };

De esta forma, ya no es necesario incluir element.h dentro de tree.h.

También debe poner guardias de inclusión alrededor de sus archivos de encabezado también.

Tengo un par de archivos de encabezado, que se reducen a:

tree.h:

#include "element.h" typedef struct tree_ { struct *tree_ first_child; struct *tree_ next_sibling; int tag; element *obj; .... } tree;

y element.h:

#include "tree.h" typedef struct element_ { tree *tree_parent; char *name; ... } element;

El problema es que ambos se referencian entre sí, por lo que el árbol necesita elementos para ser incluidos, y el elemento necesita un árbol para ser incluido.

Esto no funciona porque para definir la estructura ''árbol'', la estructura del elemento ya debe conocerse, pero para definir la estructura del elemento, se debe conocer la estructura del árbol.

¿Cómo resolver estos tipos de bucles (creo que esto puede tener algo que ver con ''forward declaration''?)?


En mi humilde opinión, la mejor manera es evitar tales bucles, ya que son un signo de couping físico que debe evitarse.

Por ejemplo (hasta donde recuerdo) el propósito de la "Heurística de diseño orientada a objetos" es evitar incluir guardias porque solo enmascaran la dependencia cíclica (física).

Otro enfoque es predecir las estructuras como esta:

element.h: struct tree_; struct element_ { struct tree_ *tree_parent; char *name; };

tree.h: struct element_; struct tree_ { struct tree_* first_child; struct tree_* next_sibling; int tag; struct element_ *obj; };



Incluir guardias es útil, pero no aborda el problema del póster, que es la dependencia recursiva de dos estructuras de datos.

La solución aquí es declarar el árbol y / o elementos como punteros a estructuras dentro del archivo de encabezado, por lo que no es necesario incluir el .h

Algo como:

struct element_; typedef struct element_ element;

En la parte superior de tree.h debería ser suficiente para eliminar la necesidad de incluir element.h

Con una declaración parcial como esta, solo puede hacer cosas con punteros de elemento que no requieren que el compilador sepa nada sobre el diseño.


La observación crucial aquí es que el elemento no necesita conocer la estructura del árbol, ya que solo tiene un puntero al mismo. Lo mismo para el árbol. Todo lo que necesita saber es que existe un tipo con el nombre relevante, no lo que contiene.

Entonces en tree.h, en lugar de:

#include "element.h"

hacer:

typedef struct element_ element;

Esto "declara" los tipos "elemento" y "estructura elemento_" (dice que existen), pero no los "define" (digamos qué son). Todo lo que necesita para almacenar un puntero-a-bla es que blah está declarado, no que está definido. Solo si desea deferencia (por ejemplo, para leer a los miembros) necesita la definición. El código en su archivo ".c" necesita hacer eso, pero en este caso sus encabezados no lo hacen.

Algunas personas crean un único archivo de encabezado que reenvía todos los tipos en un grupo de encabezados, y cada encabezado incluye eso, en lugar de determinar qué tipos realmente necesita. Eso no es esencial ni completamente estúpido.

Las respuestas sobre los guardias de inclusión son incorrectas: en general, son una buena idea, y debe leer sobre ellas y obtener un poco, pero no resuelven su problema en particular.


La respuesta correcta es usar incluir guardias, y usar declaraciones directas.

Incluir guardias

/* begin foo.h */ #ifndef _FOO_H #define _FOO_H // Your code here #endif /* end foo.h */

Visual C ++ también admite #pragma una vez. Es una directiva de preprocesador no estándar. A cambio de la portabilidad del compilador, reduce la posibilidad de colisiones de nombres de preprocesadores y aumenta la legibilidad.

Declaraciones a futuro

Adelante declara tus estructuras. Si los miembros de una estructura o clase no se necesitan explícitamente, puede declarar su existencia al comienzo de un archivo de encabezado.

struct tree; /* element.h */ struct element; /* tree.h */


Lea acerca de declaraciones futuras .

es decir.

// tree.h: #ifndef TREE_H #define TREE_H struct element; struct tree { struct element *obj; .... }; #endif // element.h: #ifndef ELEMENT_H #define ELEMENT_H struct tree; struct element { struct tree *tree_parent; ... }; #endif


La declaración directa es la manera con la que puede garantizar que habrá un tipo de estructura que se definirá más adelante.


No me gustan las declaraciones avanzadas porque son redundantes y con errores. Si desea todas sus declaraciones en el mismo lugar, debe usar los archivos include y header con guardias include.

Debería pensar en incluir como copiar y pegar, cuando el preprocesador de c encuentra una línea de #include simplemente coloca todo el contenido de myheader.h en la misma ubicación donde se encontró la línea #include.

Bueno, si escribes incluir guardias, el código de myheader.h se pegará solo una vez donde se encontró el primer #include.

Si su programa compila con varios archivos de objeto y el problema persiste, debe usar declaraciones forward entre archivos de objeto (es como usar extern) para mantener solo una declaración de tipo para todos los archivos de objeto (el compilador mezcla todas las declaraciones en la misma tabla y los identificadores deben ser único).


Una solución simple es simplemente no tener archivos de encabezado separados. Después de todo, si dependen uno del otro, nunca vas a usar uno sin el otro, entonces, ¿por qué separarlos? Puede tener archivos .c separados que usan el mismo encabezado pero proporcionan la funcionalidad más enfocada.

Sé que esto no responde a la pregunta de cómo usar todo el material de lujo correctamente, pero lo encontré útil cuando estaba buscando una solución rápida a un problema similar.