534 votes

Pourquoi devrions-nous typedef un struct si souvent en C ?

J'ai vu de nombreux programmes constitués de structures comme celle qui suit

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

Pourquoi en a-t-on besoin si souvent ? Une raison spécifique ou un domaine d'application ?

19 votes

Réponse plus complète et plus précise : stackoverflow.com/questions/612328/

0 votes

Il a des inconvénients Je pense que vous ne pouvez pas créer une liste de liens avec la structure anonyme parce que la ligne struct * ptr à l'intérieur de la structure provoquera une erreur

18 votes

La "réponse plus complète et plus précise" est Différence entre struct et typedef struct en C++. Il existe des différences significatives entre le C et le C++ dans ce domaine, ce qui fait que cette réponse n'est pas tout à fait appropriée pour une question sur le C.

562voto

unwind Points 181987

Comme l'a dit Greg Hewgill, le typedef signifie que vous ne devez plus écrire struct partout. Cela permet non seulement d'économiser des frappes au clavier, mais aussi de rendre le code plus propre puisqu'il fournit un peu plus d'abstraction.

Des trucs comme

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

devient plus propre lorsque vous n'avez pas besoin de voir le mot-clé "struct" partout, cela ressemble plus à s'il y a vraiment un type appelé "Point" dans votre langage. Ce qui, après le typedef c'est le cas je suppose.

Notez également que, bien que votre exemple (et le mien) ait omis de nommer l'élément struct Le fait de le nommer est également utile lorsque vous souhaitez fournir un type opaque. Vous auriez alors du code comme celui-ci dans l'en-tête, par exemple :

typedef struct Point Point;

Point * point_new(int x, int y);

et ensuite fournir le struct dans le fichier d'implémentation :

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

Dans ce dernier cas, vous ne pouvez pas retourner le point par valeur, puisque sa définition est cachée aux utilisateurs du fichier d'en-tête. Il s'agit d'une technique largement utilisée dans GTK+ par exemple.

UPDATE Il convient de noter qu'il existe également des projets C très réputés où cette utilisation de la fonction typedef pour cacher struct est considéré comme une mauvaise idée, le noyau Linux est probablement le projet de ce type le plus connu. Voir le chapitre 5 de Le document CodingStyle du noyau Linux pour les mots de colère de Linus. :) Mon point de vue est que le "devrait" dans le q

67 votes

Vous ne devriez pas utiliser d'identifiants avec un trait de soulignement suivi d'une lettre majuscule, ils sont réservés (voir section 7.1.3 paragraphe 1). Bien qu'il soit peu probable que cela pose un problème, il s'agit techniquement d'un comportement non défini lorsque vous les utilisez (7.1.3 paragraphe 2).

0 votes

Que pensez-vous des avantages et des inconvénients de cette syntaxe : typedef struct Point { int x, y ; } Point Ce lien : fr.wikipedia.org/wiki/Struct_(C_programming_language)#typedef discute de la question mais est ambiguë quant à savoir s'il est en faveur ou non de l'idée.

0 votes

Réponse à ma propre question : stackoverflow.com/questions/1110944/

248voto

Jerry Hicks Points 730

C'est incroyable le nombre de personnes qui se trompent à ce sujet. S'il vous plaît, ne faites pas de typedef structs en C, cela pollue inutilement l'espace de noms global qui est déjà très pollué dans les grands programmes C.

De même, les structures typées sans nom de balise sont une cause majeure de l'imposition inutile de relations d'ordre entre les fichiers d'en-tête.

Pensez-y :

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

Avec une telle définition, qui n'utilise pas de typedefs, il est possible pour une unité compilatrice d'inclure foo.h pour obtenir le fichier FOO_DEF définition. S'il ne tente pas de déréférencer le membre 'bar' de la définition de foo struct alors il n'y aura pas besoin d'inclure le fichier "bar.h".

De plus, comme les espaces de noms sont différents entre les noms de balises et les noms de membres, il est possible d'écrire un code très lisible tel que :

struct foo *foo;

printf("foo->bar = %p", foo->bar);

Puisque les espaces de noms sont séparés, il n'y a pas de conflit à nommer les variables en même temps que le nom de leur balise struct.

Si je dois maintenir votre code, je supprimerai vos structs typés.

48 votes

Ce qui est encore plus étonnant, c'est que 13 mois après que cette réponse ait été donnée, je suis le premier à l'upvoter ! Le typedef'ing structs est l'un des plus grands abus du C, et n'a pas sa place dans un code bien écrit. Le typedef est utile pour désobfusquer les types de pointeurs de fonctions alambiqués et ne sert vraiment à rien d'autre.

34 votes

Peter van der Linden s'oppose également au typage des structures dans son livre éclairant "E

37 votes

Le style de codage du noyau Linux interdit explicitement le typage des structs. Chapitre 5 : Typedefs : "C'est un erreur d'utiliser le typedef pour les structures et les pointeurs". kernel.org/doc/Documentation/CodingStyle

157voto

Michael Burr Points 181287

D'après un ancien article de Dan Saks ( http://www.ddj.com/cpp/184403396?pgno=3 ) :


Les règles du langage C pour nommer structs sont un peu excentriques, mais elles sont plutôt inoffensives. Cependant, lorsqu'elles sont étendues aux classes en C++, ces mêmes règles ouvrent de petites fissures dans lesquelles les bogues se faufiler à travers.

En C, le nom s apparaissant dans

struct s
    {
    ...
    };

est une balise. Un nom de balise n'est pas un nom de type nom. Compte tenu de la définition ci-dessus, des déclarations telles que

s x;    /* error in C */
s *p;   /* error in C */

sont des erreurs en C. Vous devez les écrire comme

struct s x;     /* OK */
struct s *p;    /* OK */

Les noms des unions et des énumérations sont également des balises plutôt que des types.

En C, les balises sont distinctes de toutes les autres noms (pour les fonctions, les types variables et constantes d'énumération). Les compilateurs C maintiennent les balises dans une table symbole qui est conceptuellement, sinon physiquement séparée de la table qui contient tous les autres noms. Ainsi, il est possible pour un programme C d'avoir à la fois une balise et un autre nom avec la même orthographe dans la même portée. Par exemple,

struct s s;

est une déclaration valide qui déclare variable s de type struct s. Cela peut pas une bonne pratique, mais les compilateurs C doivent l'accepter. Je n'ai jamais vu une raison pour laquelle le C a été conçu de cette ainsi. J'ai toujours pensé que c'était une erreur, mais c'est ainsi.

De nombreux programmeurs (dont le vôtre moi) préfèrent penser aux noms de structures comme des noms de type, donc ils définissent un alias pour la balise en utilisant un typedef. Pour exemple, en définissant

struct s
    {
    ...
    };
typedef struct s S;

vous permet d'utiliser S à la place de struct s, comme dans

S x;
S *p;

Un programme ne peut pas utiliser S comme nom de à la fois d'un type et d'une variable (ou fonction ou constante d'énumération) :

S S;    // error

C'est bien.

Le nom de la balise dans une structure, une union, ou une ou d'un enum est facultatif. De nombreux programmeurs programmeurs plient la définition de la structure dans le typedef et se passent de la balise comme dans :

typedef struct
    {
    ...
    } S;

L'article lié contient également une discussion sur la façon dont le comportement du C++, qui consiste à ne pas exiger d'un typedef peut causer de subtils problèmes de dissimulation de noms. Pour éviter ces problèmes, il est conseillé de typedef vos classes et structs en C++ également, même si, à première vue, cela semble inutile. En C++, avec le typedef le masquage du nom devient une erreur dont le compilateur vous parle plutôt qu'une source cachée de problèmes potentiels.

4 votes

Un exemple de cas où le nom de balise est le même que le nom de non-balise est dans un programme (POSIX ou Unix) avec l'instruction int stat(const char *restrict path, struct stat *restrict buf) fonction. Vous avez là une fonction stat dans l'espace de nom ordinaire et struct stat dans l'espace des noms de balises.

3 votes

Votre déclaration , S S ; // erreur .... EST FAUX Il fonctionne bien. Je veux dire que votre déclaration que "nous ne pouvons pas avoir le même nom pour typedef tag et var" est FAUX... s'il vous plaît vérifier.

71voto

Greg Hewgill Points 356191

Utilisation d'un typedef évite de devoir écrire struct chaque fois que vous déclarez une variable de ce type :

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

2 votes

Ok nous n'avons pas ce problème en C++. Alors pourquoi personne ne supprime ce problème du compilateur du C et le rend identique à celui du C++. Ok, le C++ a des domaines d'application différents et a donc des fonctionnalités avancées, mais ne pouvons-nous pas en hériter dans le C sans changer le C original ?

4 votes

Manoj, le nom de la balise ("struct foo") est nécessaire lorsque vous devez définir une structure qui se référence elle-même, par exemple le pointeur "suivant" dans une liste chaînée. Plus précisément, le compilateur met en œuvre la norme, et c'est ce que la norme dit de faire.

48 votes

Ce n'est pas un défaut du compilateur C, cela fait partie de sa conception. Ils ont changé cela pour le C++, ce qui, je pense, rend les choses plus faciles, mais cela ne signifie pas que le comportement du C est mauvais.

49voto

cschol Points 5721

Une autre bonne raison de toujours typedef enums et structs résulte de ce problème :

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

Remarquez la faute de frappe dans EnumDef dans la structure (Enu u mDef) ? Cela compile sans erreur (ou avertissement) et est (selon l'interprétation littérale de la norme C) correct. Le problème est que je viens de créer une nouvelle définition d'énumération (vide) dans ma structure. Je n'utilise pas (comme prévu) la définition précédente EnumDef.

Avec un typdef, ce type de typos aurait entraîné une erreur de compilation pour utilisation d'un type inconnu :

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

Je préconise de TOUJOURS typographier les structures et les énumérations.

Non seulement pour économiser de la frappe (sans jeu de mots ;)), mais aussi parce que c'est plus sûr.

3 votes

Pire encore, votre faute de frappe pourrait coïncider avec une autre étiquette. I

6 votes

Cette définition : 'typedef { FIRST_ITEM, SECOND_ITEM } EnumDef;' ne définit pas un enum. J'ai écrit des centaines d'énormes programmes et j'ai eu la malchance d'effectuer la maintenance de programmes écrits par d'autres. De par mon expérience, l'utilisation de typedef sur une structure ne mène qu'à des problèmes. Il faut espérer que le programmeur n'est pas handicapé au point d'avoir du mal à taper une définition complète lorsqu'il déclare une instance de structure. Le C n'est pas le Basic, donc taper quelques caractères supplémentaires n'est pas préjudiciable au fonctionnement du programme.

0 votes

Notez qu'il est généralement déconseillé d'utiliser l'option enum en tant que type, car de nombreux compilateurs émettent des avertissements étranges lorsqu'ils les utilisent "incorrectement". Par exemple, l'initialisation d'un enum à 0 pourrait donner un avertissement 'integer constant not in enum'. La déclaration anticipée d'un enum n'est pas non plus autorisée. O int (ou unsigned int ) à la place.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X