typedef struct vs définitions de la structure

Je suis un débutant en programmation C, mais je me demandais quelle est la différence entre l’utilisation de typedef pour définir une structure et ne pas utiliser typedef. Il me semble qu’il n’y a vraiment aucune différence, ils accomplissent la même chose.

struct myStruct{ int one; int two; }; 

contre.

 typedef struct{ int one; int two; }myStruct; 

Le langage commun utilise les deux:

 typedef struct X { int x; } X; 

Ce sont des définitions différentes. Pour rendre la discussion plus claire, je vais diviser la phrase:

 struct S { int x; }; typedef struct SS; 

Dans la première ligne, vous définissez l’identifiant S dans l’espace de nom de la structure (pas dans le sens C ++). Vous pouvez l’utiliser et définir des variables ou des arguments de fonction du type nouvellement défini en définissant le type de l’argument en tant que struct S :

 void f( struct S argument ); // struct is required here 

La deuxième ligne ajoute un alias de type S dans l’espace de nom global et permet ainsi d’écrire simplement:

 void f( S argument ); // struct keyword no longer needed 

Notez que les deux espaces de nom d’identifiant étant différents, définir S fois dans les structures et dans les espaces globaux n’est pas une erreur, car il ne redéfinit pas le même identifiant, mais crée un identifiant différent.

Pour faire la différence plus claire:

 typedef struct S { int x; } T; void S() { } // correct //void T() {} // error: symbol T already defined as an alias to 'struct S' 

Vous pouvez définir une fonction avec le même nom que la structure lorsque les identificateurs sont conservés dans des espaces différents, mais vous ne pouvez pas définir une fonction portant le même nom qu’un typedef lorsque ces identificateurs se heurtent.

En C ++, il est légèrement différent car les règles pour localiser un symbole ont subtilement changé. C ++ conserve toujours les deux espaces identificateurs différents, mais contrairement à C, lorsque vous définissez uniquement le symbole dans l’espace identificateur de classe, vous n’êtes pas obligé de fournir le mot clé struct / class:

  // C++ struct S { int x; }; // S defined as a class void f( S a ); // correct: struct is optional 

Quelles sont les modifications apscopes aux règles de recherche et non à la définition des identificateurs. Le compilateur recherchera la table des identifiants globaux et après que S n’a pas été trouvé, il recherchera S dans les identificateurs de classe.

Le code présenté auparavant se comporte de la même manière:

 typedef struct S { int x; } T; void S() {} // correct [*] //void T() {} // error: symbol T already defined as an alias to 'struct S' 

Après la définition de la fonction S sur la deuxième ligne, la structure S ne peut pas être résolue automatiquement par le compilateur, et pour créer un object ou définir un argument de ce type, vous devez struct mot struct clé struct :

 // previous code here... int main() { S(); struct S s; } 

struct et typedef sont deux choses très différentes.

Le mot struct clé struct est utilisé pour définir ou faire référence à un type de structure. Par exemple, ceci:

 struct foo { int n; }; 

crée un nouveau type appelé struct foo . Le nom foo est une balise ; Il n’a de sens que lorsqu’il est immédiatement précédé du mot struct clé struct , car les balises et les autres identificateurs se trouvent dans des espaces de noms distincts. (Ceci est similaire, mais beaucoup plus restreint que le concept C ++ des namespace de namespace .)

Un typedef , malgré le nom, ne définit pas un nouveau type; il crée simplement un nouveau nom pour un type existant. Par exemple, donné:

 typedef int my_int; 

my_int est un nouveau nom pour int ; my_int et int sont exactement du même type. De même, compte tenu de la définition de la struct ci-dessus, vous pouvez écrire:

 typedef struct foo foo; 

Le type a déjà un nom, struct foo . La déclaration typedef donne au même type un nouveau nom, foo .

La syntaxe vous permet de combiner une struct et un typedef en une seule déclaration:

 typedef struct bar { int n; } bar; 

Ceci est un idiome commun. Vous pouvez maintenant faire référence à ce type de structure en tant que struct bar ou simplement en tant que bar .

Notez que le nom typedef ne devient visible qu’à la fin de la déclaration. Si la structure contient un pointeur sur elle-même, utilisez la version struct pour vous y référer:

 typedef struct node { int data; struct node *next; /* can't use just "node *next" here */ } node; 

Certains programmeurs utiliseront des identificateurs distincts pour la balise struct et pour le nom typedef. À mon avis, il n’y a pas de bonne raison à cela; utiliser le même nom est parfaitement légal et rend plus clair le même type. Si vous devez utiliser différents identificateurs, utilisez au moins une convention cohérente:

 typedef struct node_s { /* ... */ } node; 

(Personnellement, je préfère omettre le typedef et faire référence au type struct bar . Le typedef sauve un peu la frappe, mais il cache le fait qu’il s’agit d’un type de structure. Si vous voulez que le type soit opaque, cela peut être un bon Si le code client se réfère au membre n par son nom, alors ce n’est pas opaque, c’est visiblement une structure, et à mon avis, il est logique de parler de structure. sur ce point, soyez prêt à lire et à comprendre le code écrit de toute façon.)

(C ++ a des règles différentes. Avec une déclaration de struct blah , vous pouvez vous référer au type simplement blah , même sans typedef. Utiliser un typedef peut rendre votre code C un peu plus C ++ – si vous pensez que c’est un bon chose.)

Une autre différence non signalée est que donner un nom à la struct (c’est-à-dire struct myStruct) vous permet également de fournir des déclarations en avant de la structure. Donc, dans un autre fichier, vous pourriez écrire:

 struct myStruct; void doit(struct myStruct *ptr); 

sans avoir à accéder à la définition. Ce que je recommande, c’est de combiner vos deux exemples:

 typedef struct myStruct{ int one; int two; } myStruct; 

Cela vous donne la commodité du nom typedef plus concis mais vous permet toujours d’utiliser le nom complet de la structure si vous en avez besoin.

En C (pas en C ++), vous devez déclarer des variables de structure telles que:

 struct myStruct myVariable; 

Pour pouvoir utiliser myStruct myVariable; au lieu de cela, vous pouvez typedef la structure:

 typedef struct myStruct someStruct; someStruct myVariable; 

Vous pouvez combiner la définition de struct et typedef dans une seule déclaration qui déclare une struct anonyme et typedef s elle.

 typedef struct { ... } myStruct; 

Si vous utilisez struct sans typedef , vous devrez toujours écrire

 struct mystruct myvar; 

C’est illégal d’écrire

 mystruct myvar; 

Si vous utilisez le typedef vous n’avez plus besoin du préfixe struct .

Dans C, les mots-clés de spécification de type des structures, unions et énumérations sont obligatoires, c.-à-d. Que vous devez toujours préfixer le nom du type (sa balise ) avec struct , union ou enum vous référant au type.

Vous pouvez vous débarrasser des mots-clés en utilisant un typedef , qui est une forme de masquage des informations, car le type réel d’un object ne sera plus visible lors de sa déclaration.

Il est donc recommandé (voir par exemple le guide de style de codage du kernel Linux , chapitre 5) de ne le faire que lorsque vous souhaitez masquer ces informations et pas seulement pour enregistrer quelques touches.

Un exemple de quand vous devriez utiliser un typedef serait un type opaque qui est seulement utilisé avec les fonctions / macros d’accesseur correspondantes.

La différence entre en jeu lorsque vous utilisez la struct .

La première façon de faire:

 struct myStruct aName; 

La seconde méthode vous permet de supprimer le mot struct clé struct .

 myStruct aName; 

Vous ne pouvez pas utiliser la déclaration forward avec la typedef struct .

La struct elle struct même est un type anonyme, vous n’avez donc pas de nom à transmettre pour déclarer.

 typedef struct{ int one; int two; } myStruct; 

Une déclaration anticipée comme celle-ci ne fonctionnera pas:

 struct myStruct; //forward declaration fails void blah(myStruct* pStruct); //error C2371: 'myStruct' : redefinition; different basic types 

Le typedef , comme c’est le cas avec d’autres constructions, est utilisé pour donner un nouveau nom à un type de données. Dans ce cas, la plupart du temps, le code est nettoyé:

 struct myStruct blah; 

contre.

 myStruct blah; 

Le code suivant crée une structure anonyme avec l’alias myStruct :

 typedef struct{ int one; int two; } myStruct; 

Vous ne pouvez pas le renvoyer sans l’alias, car vous ne spécifiez pas d’identifiant pour la structure.

Je vois des précisions à ce sujet. C et C ++ ne définissent pas les types différemment. C ++ était à l’origine rien de plus qu’un ensemble supplémentaire d’inclusions sur C.

Le problème que pratiquement tous les développeurs C / C ++ rencontrent aujourd’hui est le suivant: a) les universités n’enseignent plus les fondamentaux, et b) les personnes ne comprennent pas la différence entre une définition et une déclaration.

La seule raison pour laquelle de telles déclarations et définitions existent est que l’éditeur de liens peut calculer les décalages d’adresses pour les champs de la structure. C’est pourquoi la plupart des gens s’en sortent avec du code écrit de manière incorrecte, car le compilateur est capable de déterminer l’adressage. Le problème survient lorsque quelqu’un essaie de faire quelque chose d’avance, comme une queue, une liste chaînée ou un mécanisme de corrélation.

Une déclaration commence par «struct», une définition commence par «typedef».

De plus, une structure a une étiquette de déclaration directe et une étiquette définie. La plupart des gens ne le savent pas et utilisent l’étiquette de déclaration directe comme étiquette de définition.

Faux:

 struct myStruct { int field_1; ... }; 

Ils ont simplement utilisé la déclaration forward pour étiqueter la structure – le compilateur en est maintenant conscient – mais ce n’est pas un type défini. Le compilateur peut calculer l’adressage, mais ce n’est pas comme cela qu’il était destiné à être utilisé, pour des raisons que je montrerai brièvement.

Les personnes qui utilisent cette forme de déclaration doivent toujours mettre «struct» dans pratiquement toutes les références – parce que ce n’est pas un nouveau type officiel.

Au lieu de cela, toute structure qui ne fait pas référence elle-même doit être déclarée et définie de cette manière uniquement:

 typedef struct { field_1; ... }myStruct; 

Maintenant, il s’agit d’un type réel et, lorsqu’il est utilisé, vous pouvez utiliser at ‘myStruct’ sans avoir à append le mot ‘struct’.

Si vous souhaitez une variable de pointeur pour cette structure, incluez une étiquette secondaire:

 typedef struct { field_1; ... }myStruct,*myStructP; 

Maintenant, vous avez une variable de pointeur sur cette structure, personnalisée pour elle.

DÉCLARATION AVANCÉE–

Maintenant, voici les trucs de fantaisie, comment fonctionne la déclaration anticipée. Si vous souhaitez créer un type qui se réfère à lui-même, comme une liste chaînée ou un élément de queue, vous devez utiliser une déclaration de transfert. Le compilateur ne considère pas la structure définie jusqu’à ce qu’il atteigne le point-virgule à la toute fin, il est donc simplement déclaré avant ce point.

 typedef struct myStructElement { myStructElement* nextSE; field_1; ... }myStruct; 

Maintenant, le compilateur sait que même s’il ne connaît pas encore le type entier, il peut toujours le référencer en utilisant la référence directe.

S’il vous plaît déclarer et typedef vos structures correctement. Il y a en fait une raison.

Avec ce dernier exemple, vous omettez le mot-clé struct lorsque vous utilisez la structure. Donc, partout dans votre code, vous pouvez écrire:

 myStruct a; 

au lieu de

 struct myStruct a; 

Cela permet d’économiser un peu de frappe et peut-être plus lisible, mais c’est une question de goût