¿Qué es ‘forward declaration’ y la diferencia entre ‘typedef struct X’ y ‘struct X’?

Soy un principiante en la progtwigción de C y sé la diferencia entre la statement de tipo de estructura y la statement de estructura typedef. Encontré una respuesta diciendo que si definimos una estructura como:

typedef struct { some members; }struct_name; 

Entonces será como proporcionar un alias a una estructura anónima (ya que no tiene un nombre de etiqueta). Por lo tanto, no se puede usar para la statement directa. No sé lo que significa la “statement directa”.

Además, quería saberlo para el siguiente código:

 typedef struct NAME { some members; }struct_alias; 

¿Hay alguna diferencia entre NAME y struct_alias ? ¿O ambos son iguales ya que struct_alias es un alias de struct NAME?

Además, ¿podemos declarar una variable de tipo struct NAME como estas?

 struct_alias variable1; 

y / o como:

 struct NAME variable2; 

o como:

 NAME variable3; 

las declaraciones de struct hacia adelante pueden ser útiles cuando necesita tener declaraciones de estructura de bucle. Ejemplo:

 struct a { struct b * b_pointer; int c; }; struct b { struct a * a_pointer; void * d; }; 

Cuando struct a se declara, aún no conoce las especificaciones de struct b , pero puede reenviarla.

Cuando tipea una estructura anónima, el comstackdor no le permitirá usar su nombre antes de typedef.

Esto es ilegal:

 struct a { b * b_pointer; int c; }; typedef struct { struct a * a_pointer; void * d; } b; // struct b was never declared or defined 

Esto aunque es legal:

 struct a { struct b * b_pointer; int c; }; typedef struct b { struct a * a_pointer; void * d; } b; // struct b is defined and has an alias type called b 

Asi es esto:

 typedef struct bb; // the type b referes to a yet undefined type struct b struct a { b * struct_b_pointer; int c; }; struct b { struct a * a_pointer; void * d; }; 

Y esto:

 typedef int b; struct a { struct b * struct_b_pointer; b b_integer_type; int c; }; struct b { struct a * a_pointer; void * d; }; // struct b and b are two different types all together. Note: this is not allowed in C++ 

La statement directa es una promesa de definir algo que usted hace a un comstackdor en el punto donde no se puede hacer la definición. El comstackdor puede usar su palabra para interpretar otras declaraciones que de otro modo no podría interpretar.

Un ejemplo común es una struct diseñada para ser un nodo en una lista vinculada: debe poner un puntero a un nodo en la struct , pero el comstackdor no le permitirá hacerlo sin una statement directa o una etiqueta:

 // Forward declaration struct element; typedef struct { int value; // Use of the forward declaration struct element *next; } element; // Complete definition 

y entonces no se puede usar para la statement directa

Creo que el punto del autor era que darle una etiqueta a tu struct sería equivalente a una statement a futuro:

 typedef struct element { int value; // No need for a forward declaration here struct element *next; } element; 

La statement directa es una statement que precede a una definición real, generalmente con el propósito de poder hacer referencia al tipo declarado cuando la definición no está disponible. Por supuesto, no todo se puede hacer con la estructura declarada no definida, pero en cierto contexto es posible usarla. Tal tipo se llama incompleto , y hay una serie de restricciones en su uso. Por ejemplo:

 struct X; // forward declaration void f(struct X*) { } // usage of the declared, undefined structure // void f(struct X) { } // ILLEGAL // struct X x; // ILLEGAL // int n =sizeof(struct X); // ILLEGAL // later, or somewhere else altogether struct X { /* ... */ }; 

Esto puede ser útil, por ejemplo, para romper dependencias circulares, o reducir el tiempo de comstackción, ya que las definiciones suelen ser significativamente más grandes, por lo que se necesitan más recursos para analizarlo.

En su ejemplo, struct NAME y struct_alias son de hecho equivalentes.

 struct_alias variable1; struct NAME variable2; 

son correctos;

 NAME variable3; 

no es así, ya que en C se requiere la palabra clave struct .

struct_alias y struct NAME son iguales, struct_alias es un alias para struct NAME

Ambos son iguales y permitidos

 struct_alias variable1; struct NAME variable1; 

esto es ilegal

 NAME variable3; 

Ver este artículo en la statement de Forward

Como otros dijeron antes, una statement directa en C / C ++ es la statement de algo con la definición real no disponible. Es una statement que le dice al comstackdor “hay un tipo de datos ABC”.

Vamos a pretender que este es un encabezado para algunos key / value store my_dict.h :

 ... struct my_dict_t; struct my_dict_t* create(); char* get_value(const struct my_dict_t* dict, const char* name); char* insert(struct my_dict_t* dict, const char* name, char* value); void destroy(struct my_dict_t* dict); ... 

No sabes nada sobre my_dict_t , pero en realidad, para usar la tienda no necesitas saber:

 #include "my_dict.h" ... struct my_dict_t* dict = create(); if(0 != insert(dict, "AnEntry", strdup("AValue"))) { ... } ... 

La razón de esto es: solo está utilizando POINTERS para la estructura de datos.

Los punteros son solo números, y para lidiar con ellos no es necesario saber a qué apuntan.

Esto solo importará si intenta acceder a ellos de verdad, como

 struct my_dict_t* dict = create(); printf("%s\n", dict->value); /* Impossible if only a forward decl is available */ 

Entonces, para implementar las funciones, necesita una definición real de my_struct_t . Puede hacer esto en el archivo de origen my_dict.c manera:

 #include "my_dict.h" struct my_dict_t { char* value; const char* name; struct my_dict_t* next; } struct my_dict_t* create() { return calloc(1, sizeof(struct my_dict_t)); } 

Esto es útil para varias situaciones, como

  • Para resolver dependencias de tipo circular, como explicó Sergei L.
  • Para la encapsulación, como en el ejemplo anterior.

Entonces, la pregunta que queda es: ¿Por qué no podemos omitir la statement directa cuando utilizamos las funciones anteriores? Al final, sería suficiente para el comstackdor saber que todos los dict son punteros.

Sin embargo, el comstackdor realiza comprobaciones de tipo: necesita verificar que no hagas algo como

 ... int i = 12; char* value = get_value(&i, "MyName"); ... 

No necesita saber cómo se ve my_dict_t , pero necesita saber que &i no es el tipo de puntero que get_value() espera.