web-dev-qa-db-fra.com

typedef struct vs définitions de structure

Je suis un débutant en programmation C, mais je me demandais quelle était la différence entre utiliser typedef pour définir une structure et ne pas utiliser typedef. Il me semble qu'il n'y a pas vraiment de différence, ils atteignent le même objectif.

struct myStruct{
    int one;
    int two;
};

vs.

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

L'idiome commun utilise à la fois:

typedef struct X { 
    int x; 
} X;

Ce sont des définitions différentes. Pour clarifier la discussion, je vais scinder la phrase:

struct S { 
    int x; 
};

typedef struct S S;

Dans la première ligne, vous définissez l'identificateur S dans l'espace de nom de structure (pas au 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 comme suit: 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 vous permet donc d’écrire simplement:

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

Notez que, puisque les deux espaces de nom d'identifiant sont différents, définir S à la fois dans les structures et dans les espaces globaux n'est pas une erreur, car il ne s'agit pas de redéfinir le même identificateur, mais de créer un identificateur différent à un emplacement différent.

Pour rendre 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 portant le même nom que la structure car les identifiants 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 lors de la collision de ces identifiants.

En C++, cela est légèrement différent car les règles pour localiser un symbole ont changé subtilement. C++ conserve toujours les deux espaces d'identificateur différents, mais contrairement à C, lorsque vous définissez uniquement le symbole dans l'espace d'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

Quels sont les changements sont les règles de recherche, pas où les identificateurs sont définis. Le compilateur effectuera une recherche dans la table d'identificateurs globaux et, après avoir trouvé S, il recherchera S dans les identificateurs de classe.

Le code présenté précédemment 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 dans la deuxième ligne, la struct S ne peut pas être résolue automatiquement par le compilateur. Pour créer un objet ou définir un argument de ce type, vous devez inclure le mot clé struct:

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

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

Le mot clé struct permet de définir ou de 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 un tag; cela n'a de sens que lorsqu'il est immédiatement précédé du mot clé struct, car les balises et autres identifiants sont séparés espaces de nom. (Ceci est similaire au concept C++ de namespaces.)

Une typedef, malgré son nom, ne définit pas un nouveau type; il crée simplement un nouveau nom pour un type existant. Par exemple, étant 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, étant donné la définition de 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 typedef en une seule déclaration:

typedef struct bar {
    int n;
} bar;

C'est un idiome commun. Vous pouvez maintenant appeler ce type de structure soit struct bar, soit simplement 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, vous devez utiliser la version struct pour s'y référer:

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

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

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

(Personnellement, je préfère omettre le typedef et me référer au type struct bar. Le typedef enregistrer un peu de frappe, mais il cache le fait qu'il s'agit d'un type de structure. Si vous voulez Si le code client fait référence au membre n par son nom, il n'est pas opaque, il s'agit visiblement d'une structure et, à mon avis, il est logique de référez-vous à cela en tant que structure. Mais beaucoup de programmeurs intelligents ne sont pas d’accord avec moi 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 appeler le type simplement blah, même sans typedef. Utiliser un typedef peut rendre votre code C un peu plus semblable à C++ - - si vous pensez que c'est une bonne chose.)

112
Keith Thompson

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

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

sans avoir à avoir accès à la définition. Je vous recommande de combiner vos deux exemples:

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

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

89

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

struct myStruct myVariable;

Afin de pouvoir utiliser myStruct myVariable; à la place, vous pouvez typedef the struct:

typedef struct myStruct someStruct;
someStruct myVariable;

Vous pouvez combiner les définitions struct et typedefs dans une seule instruction qui déclare anonymes les noms struct et typedefs.

typedef struct { ... } myStruct;
55
Mehrdad Afshari

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

struct mystruct myvar;

C'est illégal d'écrire

mystruct myvar;

Si vous utilisez la variable typedef, vous n'avez plus besoin du préfixe struct.

30
RED SOFT ADAIR

En C, les mots clés spécifiant le type des structures, des unions et des énumérations sont obligatoires, c’est-à-dire que vous devez toujours préfixer le nom du type (sa balise ) avec struct, union ou enum lorsque vous vous référez au type.

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

Il est donc recommandé (voir par exemple le Guide du style de codage du noyau Linux , Chapitre 5) de ne le faire que lorsque vous désirez réellement pour masquer cette information et pas seulement pour enregistrer quelques frappes.

Un exemple d'utilisation de typedef serait un type opaque utilisé uniquement avec les fonctions/macros accesseurs correspondantes.

23
Christoph

Vous ne pouvez pas utiliser la déclaration anticipée avec le typedef struct.

La struct elle-même est un type anonyme, vous n'avez donc pas de nom à transmettre.

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

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

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types
7
Yochai Timmer

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

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

Vous ne pouvez pas le faire sans l'alias car vous ne spécifiez pas d'identificateur pour la structure.

6
Wronski

La différence entre lorsque vous utilisez le struct.

La première façon de faire:

struct myStruct aName;

La deuxième façon vous permet de supprimer le mot clé struct.

myStruct aName;
5
jjnguy

La typedef, comme avec d'autres constructions, est utilisée pour attribuer un nouveau nom à un type de données. Dans ce cas, il s’agit surtout de rendre le code plus propre:

struct myStruct blah;

vs.

myStruct blah;
4
RC.

Je vois quelques éclaircissements nécessaires à ce sujet. C et C++ ne définissent pas les types différemment. C++ n’était à l’origine rien de plus qu’un ensemble supplémentaire d’inclusions au-dessus de C.

Le problème que rencontrent pratiquement tous les développeurs C/C++ aujourd'hui est que a) les universités n'enseignent plus les bases et b) que les gens ne comprennent pas la différence entre une définition et une déclaration.

De telles déclarations et définitions existent uniquement pour permettre à l'éditeur de liens de calculer les décalages d'adresse par rapport aux champs de la structure. C'est pourquoi la plupart des gens s'en sortent avec du code réellement mal écrit - car le compilateur est capable de déterminer l'adressage. Le problème se pose lorsque quelqu'un essaie de faire quelque chose d'avancé, comme une file d'attente, une liste chaînée, ou une liaison avec une structure de système d'exploitation.

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

En outre, une structure a une étiquette de déclaration en aval et une étiquette définie. La plupart des gens ne le savent pas et utilisent l’étiquette de déclaration avant comme étiquette de définition.

Faux:

struct myStruct
   {
   int field_1;
   ...
   };

Ils ont juste utilisé la déclaration forward pour étiqueter la structure - donc maintenant le compilateur en est conscient - mais ce n'est pas un type défini réel. Le compilateur peut calculer l'adressage - mais ce n'est pas ainsi qu'il a été conçu, pour des raisons que je vais montrer tout à l'heure.

Les personnes qui utilisent cette forme de déclaration doivent toujours mettre 'struct' pratiquement dans chaque référence - car il ne s'agit pas d'un nouveau type officiel.

Au lieu de cela, toute structure qui ne fait pas référence à elle-même, devrait être déclarée et définie de cette façon uniquement:

typedef struct
   {
   field_1;
   ...
   }myStruct;

Il s’agit maintenant d’un type réel et, lorsqu’il est utilisé, vous pouvez utiliser la commande at en tant que "myStruct" sans avoir à le précéder du mot "struct".

Si vous voulez une variable de pointeur sur cette structure, incluez une étiquette secondaire:

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

Vous avez maintenant une variable de pointeur sur cette structure, personnalisée.

DÉCLARATION À SUIVRE--

Maintenant, voici ce qui est compliqué, comment fonctionne la déclaration forward. Si vous souhaitez créer un type qui se réfère à lui-même, par exemple une liste liée ou un élément de file d'attente, 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, elle est donc simplement déclarée avant ce point.

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

Maintenant, le compilateur sait que, même s'il ne sait pas encore quel est le type complet, il peut toujours le référencer à l'aide de la référence directe.

Veuillez déclarer et dactylographier correctement vos structures. Il y a en fait une raison.

3
CodeGuru

Dans 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 économise du texte et pourrait être plus lisible, mais c'est une question de goût

2
shodanex