Pourquoi utiliser différentes balises et typedef différentes pour une structure?

En code C, j’ai vu ce qui suit:

typedef struct SomeStructTag { // struct members } SomeStruct; 

Je ne comprends pas pourquoi c’est différent de:

 typedef struct SomeStruct { // struct members } SomeStruct; 

Quel est l’intérêt d’utiliser un nom particulier pour le type et de le taper dans un nom de type différent?

Dans la plupart des cas, le même nom pourrait être utilisé sans aucun problème. La pratique consistant à utiliser des noms différents dans le code remonte probablement à l’époque précédant la normalisation ANSI, lorsque de nombreux compilateurs acceptaient des dialectes essentiellement compatibles de quelque chose qui ressemblait à C. Certains concepts et certaines fonctionnalités s’appliquaient à presque tous les compilateurs, d’autres sur la plupart (mais échec sur un nombre significatif), et d’autres ne fonctionneraient que sur quelques-uns. Bien que, depuis de nombreuses années, la norme indique clairement qu’il existe différents espaces de noms pour les types, les balises de structure et que, pour les membres de chaque structure, certains compilateurs des débuts ne reconnaissaient pas tous ces espaces de noms comme distincts. a été écrit qui était censé fonctionner même avec de tels compilateurs, et les gens ont tendance à écrire du code qui ressemble à un autre code qu’ils ont vu.

Je pense que la principale objection à l’utilisation du même identifiant compte tenu des règles d’aujourd’hui serait le principe selon lequel des identifiants distincts doivent avoir l’air distinct, et il faut généralement éviter d’utiliser deux identifiants sémantiquement équivalents dans un but donné. Il est bien d’utiliser deux identificateurs distincts à certaines fins s’il existe une distinction sémantique claire entre eux (par exemple, “le type le plus rapide ayant au moins 32 bits” et un type “exactement 32 bits”), mais il n’est pas bon que le code utilise deux types différents. identifiants pour le même but essentiellement au hasard. Si on déclare typedef struct FOO {...} FOO; , alors struct FOO et FOO seront des identifiants distincts qui semblent devoir être interchangeables. S’il y a des cas particuliers où l’un est supposé utiliser l’un ou l’autre et que les noms sont distincts, le fait de taper struct là où il ne faut pas, ou inversement, provoquera une erreur de compilation. Si les noms correspondent, les choses se comstackraient et rien ne serait prévenu que l’utilisation du nom n’était pas compatible avec son utilisation ailleurs.

Incidemment, bien que les balises struct soient disponibles dans le monde entier, il n’est souvent pas nécessaire d’utiliser une balise plus d’une fois. Même si une structure est supposée contenir des pointeurs auto-référentiels, on peut, en ANSI C, la déclarer:

 typedef struct NODE_S NODE; struct NODE_S { NODE *parent,*left,*right; }; 

sans avoir à faire référence au type sous le nom de struct NODE_S n’importe où sauf la ligne particulière déclarant le nom du typedef. Partout ailleurs, on peut simplement utiliser directement le nom typedef.

La raison en est que les balises struct et les noms de typedef se trouvent dans des espaces de noms différents en C. Vous pouvez uniquement utiliser une balise struct après le mot clé struct, . Il est donc pratique de savoir laquelle est laquelle.

En C ++, ils se trouvent dans le même espace de noms, le problème ne se pose donc pas. Ce qui signifie également que votre deuxième exemple peut ne pas être légal C ++. Votre premier exemple est légal dans les deux langues.

Il y a une raison fonctionnelle d’utiliser une balise différente entre le typedef et la struct. C’est lorsque vous créez une structure de liste chaînée, où l’un des champs de votre structure est un pointeur sur une instance de la structure en cours de définition. Étant donné que l’instruction n’est pas encore complète, vous ne pouvez pas utiliser le nom typedef dans l’instruction.

Par exemple:

 typedef struct { link_t* next; void* data; } link_t; 

ne fonctionnera pas, car vous essayez d’utiliser link_t avant que le compilateur ait vu le symbole. Au lieu de cela, vous écrivez ainsi:

 typedef struct LL { struct LL* next; void* data; } link_t; 

et la structure LL est connue du compilateur une ligne avant de l’utiliser.

La norme C n’a aucune raison d’utiliser des noms différents pour les balises et les types. Ils ne peuvent pas interférer les uns avec les autres car ils se trouvent dans des espaces de noms différents et ne peuvent pas être utilisés aux mêmes endroits.

Par conséquent, la seule raison d’utiliser des noms différents serait pour la psychologie humaine. Bien que d’autres réponses aient suggéré que l’utilisation de noms différents pour les balises et les types facilite leur repérage dans le code, il n’y a jamais d’ambiguïté car les noms de balises de structure apparaissent uniquement après struct , et seuls les noms de balises de structure apparaissent après struct . Plus que cela, si elles sont identiques, vous ne pouvez pas utiliser accidentellement l’un où vous avez l’intention de l’autre. Il n’est donc pas nécessaire d’utiliser des noms différents.

Le fait que C ++ ait adopté les balises de structure en tant que noms de classe montre qu’il est inutile qu’il y ait une différence.

Le fait est que SomeStruct sera alors un nom de type approprié. Donc vous n’êtes pas obligé de taper

 struct SomeStructTag my_struct; 

Dans les déclarations:

 SomeStruct my_struct; 

Autre que cela, IMHO le _t ajouté à un nom est synonyme de type, pas de balise.