Je suis un débutant en programmation C et je connais la différence entre la déclaration de type struct et la déclaration de structure typedef. Je suis tombé sur une réponse disant que si nous définissons une structure comme:
typedef struct
{
some members;
}struct_name;
Ensuite, ce sera comme si vous fournissiez un alias à une structure anonyme (car elle ne possède pas de nom de balise). Donc, il ne peut pas être utilisé pour la déclaration anticipée. Je ne sais pas ce que signifie "déclaration anticipée".
Aussi, je voulais savoir que pour le code suivant:
typedef struct NAME
{
some members;
}struct_alias;
Existe-t-il une différence entre NAME
et struct_alias
? Ou sont-ils égaux, puisque struct_alias est un alias de struct NAME?
De plus, pouvons-nous déclarer une variable de type struct NAME
comme celles-ci:
struct_alias variable1;
et/ou comme:
struct NAME variable2;
ou comme:
NAME variable3;
Les déclarations forward struct
peuvent être utiles lorsque vous devez avoir des déclarations de structure en boucle. Exemple:
struct a {
struct b * b_pointer;
int c;
};
struct b {
struct a * a_pointer;
void * d;
};
Lorsque struct a
est déclaré, il ne connaît pas encore les spécifications de struct b
, mais vous pouvez transférer la référence.
Lorsque vous tapez une structure anonyme, le compilateur ne vous autorisera pas à utiliser son nom avant la typedef.
C'est illégal:
struct a {
b * b_pointer;
int c;
};
typedef struct {
struct a * a_pointer;
void * d;
} b;
// struct b was never declared or defined
Ceci est cependant légal:
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
Alors est-ce:
typedef struct b b;
// 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;
};
Et ça:
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++
Déclaration en aval est une promesse de définir quelque chose que vous apportez à un compilateur au moment où la définition ne peut pas être faite. Le compilateur peut utiliser votre mot pour interpréter d'autres déclarations qu'il ne pourrait pas interpréter autrement.
Un exemple courant est une struct
conçue pour être un nœud dans une liste chaînée: vous devez placer un pointeur sur un nœud dans la struct
, mais le compilateur ne vous le permettrait pas sans une déclaration ou une balise:
// Forward declaration
struct element;
typedef struct {
int value;
// Use of the forward declaration
struct element *next;
} element; // Complete definition
et donc il ne peut pas être utilisé pour la déclaration à terme
Je pense que l'auteur disait que donner une balise à struct
équivaudrait à une déclaration anticipée:
typedef struct element {
int value;
// No need for a forward declaration here
struct element *next;
} element;
Déclaration en aval est une déclaration précédant une définition réelle, généralement dans le but de pouvoir référencer le type déclaré lorsque la définition n'est pas disponible. Bien sûr, tout ne peut pas être fait avec la structure déclarée non définie, mais dans certains contextes, il est possible de l'utiliser. Ce type s'appelle incomplet , et son utilisation est soumise à un certain nombre de restrictions. Par exemple:
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 { /* ... */ };
Cela peut être utile, par exemple. pour rompre les dépendances circulaires ou réduire le temps de compilation, car les définitions sont généralement beaucoup plus volumineuses et nécessitent donc plus de ressources pour les analyser.
Dans votre exemple, struct NAME
et struct_alias
sont effectivement équivalents.
struct_alias variable1;
struct NAME variable2;
sont corrects;
NAME variable3;
n'est pas, comme en C, le mot clé struct
est requis.
struct_alias
et struct NAME
sont identiques, struct_alias
est un alias de struct NAME
Ces deux sont les mêmes et autorisés
struct_alias variable1;
struct NAME variable1;
c'est illégal
NAME variable3;
Voir cet article sur Déclaration forward
Comme d'autres l'ont déjà indiqué, une déclaration anticipée en C/C++ est la déclaration de quelque chose dont la définition actuelle n'est pas disponible. C'est une déclaration disant au compilateur "qu'il y a un type de données ABC".
Supposons qu'il s'agit d'un en-tête pour un magasin de clés/valeurs 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);
...
Vous ne savez rien sur my_dict_t
, mais en réalité, pour utiliser le magasin, vous n'avez pas besoin de savoir:
#include "my_dict.h"
...
struct my_dict_t* dict = create();
if(0 != insert(dict, "AnEntry", strdup("AValue"))) {
...
}
...
La raison en est que vous utilisez uniquement des indicateurs avec la structure de données.
Les POINTERS ne sont que des chiffres, et pour les traiter, vous n'avez pas besoin de savoir à quoi ils pointent.
Cela n’aura d’importance que si vous essayez d’y accéder, comme
struct my_dict_t* dict = create();
printf("%s\n", dict->value); /* Impossible if only a forward decl is available */
Donc, pour implémenter les fonctions, vous avez besoin d’une définition réelle de my_struct_t
. Vous pouvez le faire dans le fichier source my_dict.c
comme ceci:
#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));
}
C'est pratique pour plusieurs situations, comme
La question qui reste est donc la suivante: pourquoi ne pouvons-nous pas simplement omettre la déclaration anticipée lorsque nous utilisons les fonctions ci-dessus? En fin de compte, il suffirait au compilateur de savoir que tous les dict
sont des pointeurs.
Cependant, le compilateur effectue des vérifications de type: Il doit vérifier que vous ne faites pas quelque chose comme:
...
int i = 12;
char* value = get_value(&i, "MyName");
...
Il n'a pas besoin de savoir à quoi ressemble my_dict_t
, mais il doit savoir que &i
n'est pas le type de pointeur que get_value()
attend.