Cela donne une erreur en C++ mais pas en C:
typedef struct nodes
{
int data;
struct node *next;
}node;
Il donne l'erreur suivante en C++.
/home/DS cpp/linkedlist.cpp|10|error: conflicting declaration ‘typedef struct nodes node’|
/home/DS cpp/linkedlist.cpp|9|error: ‘struct node’ has a previous declaration as ‘struct node’|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
Pour que cela fonctionne en C++, je dois le changer en ceci:
typedef struct node
{
int data;
struct node *next;
}node;
Je ne comprends pas pourquoi cela se produit, je veux connaître l'ordre d'exécution en C et C++ afin de pouvoir le comprendre.
Analysons un peu votre code:
typedef struct nodes
{
int data;
struct node *next;
}node;
Cela déclare et définit struct nodes
, Un type à deux membres, et déclare un alias de type afin que nous puissions y faire référence uniquement comme node
.
Maintenant, en C++, la déclaration de membre struct node *next
déclare automatiquement un type appelé node
. Cela entre alors en conflit avec votre typedef
cible node
: c'est comme si vous essayiez de donner deux types au même nom.
En C, il n'y a pas de conflit, car le type appelé node
ne peut en fait être appelé que struct node
.
Le deuxième extrait a fonctionné parce que, puisque lors de l'analyse de la déclaration de membre struct node
Existe déjà, aucun nouveau type n'y est déclaré en avant… et puisque tout ce que vous faites est de le renommer dans le même typedef
instruction, C++ ne se soucie pas vraiment, sachant que tout est du même type (struct T
is T
; la différence est dans la syntaxe, pas dans le nom).
[C++11: 7.1.3/3]:
Dans une étendue non-classe donnée, un spécificateurtypedef
peut être utilisé pour redéfinir le nom de tout type déclaré dans cette étendue pour faire référence au type auquel il se réfère déjà. [Exemple:typedef struct s { / ... / } s; typedef int I; typedef int I; typedef I I;
- exemple de fin]
[C++11: 7.1.3/6]:
Dans une portée donnée, un spécificateurtypedef
ne doit pas être utilisé pour redéfinir le nom d'un type déclaré dans cette portée pour faire référence à un autre type. [Exemple:class complex { / ... / }; typedef int complex; // error: redefinition
- exemple de fin]
Bien sûr, en C++, tout cela est théorique et vous devez simplement écrire:
struct node
{
int data;
node* next;
};
Vous n'avez pas besoin de typedef
- loin le spécificateur de type élaboré struct
.
L'exemple C que vous avez donné devrait être une erreur. Vous utilisez un nom de balise (node
) que vous n'avez pas défini avec struct node
.
Compte tenu de ces deux choix, le second est celui à utiliser. Je préfère un peu d'économie:
typedef struct node_t
{
int data;
struct node_t *next;
} node_t;
En C ou C++, les noms de balises ont leur propre espace de noms, il n'y a donc aucun problème à utiliser le même nom pour la balise et le nom de typedef. En C, cela vous permet d'utiliser soit node_t
ou struct node_t
pour faire référence à ce type de structure. C++ recherchera les noms de balises pour un nom de type si un nom de type déclaré n'existe pas, donc la double définition ci-dessus n'est pas nécessaire, mais ne fait pas de mal.
Dans les deux langues, l'explicite struct node_t
la version est requise à tout moment avant que le type ne soit complètement défini, donc toute auto-référence et toute référence directe utiliseront la version struct
. Je préfère cela dans les fichiers d'en-tête, principalement parce que cela réduit les problèmes avec l'ordre de #include
directives.
PS: cela fonctionne fonctionne dans les deux langues (voir la réponse de LRIO pour les pointeurs dans la norme C++ 11) et a été utilisé dans suffisamment de fichiers d'en-tête C++ bilingues et même purs qu'il est peu probable qu'il disparaisse bientôt) donc c'est une approche très simple qui fonctionne dans les deux langues.