Question

J'ai vu de nombreux programmes constitués de structures comme celle ci-dessous

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

elem user;

Pourquoi est-ce nécessaire si souvent? Une raison spécifique ou une zone applicable?

Était-ce utile?

La solution

Comme Greg Hewgill l’a dit, le typedef signifie que vous n’aurez plus à écrire struct partout. Cela permet non seulement d’économiser des frappes au clavier, mais aussi de rendre le code plus propre car 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 quand vous n'avez pas besoin de voir le " struct " mot-clé partout, il semble plutôt qu'il existe un type appelé "Point". dans ta langue. Après le typedef , c’est le cas, je suppose.

Notez également que même si votre exemple (et le mien) ont omis de nommer la struct elle-même, il est également utile de le nommer lorsque vous souhaitez fournir un type opaque. Ensuite, vous aurez un code comme celui-ci dans l'en-tête, par exemple:

typedef struct Point Point;

Point * point_new(int x, int y);

, puis fournissez la définition 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 renvoyer le point par valeur car sa définition est masquée pour les utilisateurs du fichier d'en-tête. C'est une technique largement utilisée par exemple dans GTK + .

UPDATE Notez qu'il existe également des projets C très prisés dans lesquels l'utilisation de typedef pour masquer struct est considérée comme une mauvaise idée. Le noyau Linux est probablement le projet le plus connu. Voir le chapitre 5 du du document CodingStyle du noyau Linux . pour les mots en colère de Linus. :) Mon argument est que le " devrait " dans la question est peut-être pas gravé dans la pierre, après tout.

Autres conseils

C'est incroyable de voir combien de personnes se trompent. VEUILLEZ ne pas typer les structures en C, cela pollue inutilement l’espace de nom global qui est généralement déjà très pollué dans les grands programmes C.

De plus, les structures typedef ayant un nom de balise sont une cause majeure d'imposition inutile de relations de classement entre les fichiers d'en-tête.

Considérez:

#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, sans utilisation de typedefs, il est possible pour une unité compiland d'inclure foo.h pour obtenir la définition FOO_DEF . S'il n'essaie pas de déréférencer le membre 'bar' de la structure foo , il ne sera pas nécessaire d'inclure le "bar.h". fichier.

De plus, étant donné que les espaces de noms diffèrent 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);

Comme les espaces de noms sont séparés, il n'y a pas de conflit entre les variables de nommage qui coïncident avec leur nom de balise struct.

Si je dois conserver votre code, je supprimerai vos structures typedef.

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

  

Les règles de langage C pour nommer   les structures sont un peu excentriques, mais   ils sont plutôt inoffensifs. Cependant, quand   étendu aux classes en C ++, ces mêmes   les règles ouvrent de petites fissures pour les bugs   ramper à travers.

     

En C, le nom apparaît dans

struct s
    {
    ...
    };
     

est une balise. Un nom de tag n'est pas un type   pré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 tags sont distincts de tous les autres   noms (pour les fonctions, les types,   variables et constantes d'énumération).   Les compilateurs C maintiennent les balises dans un symbole   table qui est conceptuellement sinon   physiquement séparé 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 pourrait   ne pas être une bonne pratique, mais les compilateurs C   doit l'accepter. Je n'ai jamais vu de   raison pour laquelle C a été conçu cette   façon. J'ai toujours pensé que c'était un   erreur, mais voilà.

     

De nombreux programmeurs (y compris le vôtre   vraiment) préfère penser aux noms de structure   comme noms de types, ils définissent donc un alias   pour la balise utilisant un typedef. Pour   par exemple, définir

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 un type et une variable (ou   constante de fonction ou d’énumération):

S S;    // error
     

C'est bien.

     

Le nom de la balise dans une structure, une union ou   la définition enum est facultative. Beaucoup   les programmeurs plient la définition de la structure   dans le typedef et se passer de la   tag tout à fait, comme dans:

typedef struct
    {
    ...
    } S;

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

L'utilisation d'un typedef évite d'avoir à é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

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

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

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

Avec un typdef, des fautes de frappe similaires auraient entraîné des erreurs de compilation pour l'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 vous recommanderais TOUJOURS de dactylographier les structures et les énumérations.

Non seulement pour sauver une frappe (sans jeu de mots;)), mais parce que c'est plus sûr.

style de codage du noyau Linux Le chapitre 5 présente de grands avantages et inconvénients (principalement des inconvénients) concernant en utilisant typedef .

  

N'utilisez pas d'éléments comme "vps_t".

     

C'est une erreur d'utiliser "typedef" pour les structures et les pointeurs. Quand vous voyez un

vps_t a;
     

dans le source, qu'est-ce que cela signifie?

     

En revanche, s'il est écrit

struct virtual_container *a;
     

vous pouvez réellement dire ce que "&"; un " est.

     

Beaucoup de gens pensent que les corrections de type "facilitent la lisibilité". Pas si. Ils ne sont utiles que pour:

     

(a) objets totalement opaques (où la typedef est activement utilisée pour masquer l'objet).

     

Exemple: "pte_t" etc. des objets opaques auxquels vous ne pouvez accéder qu'en utilisant les fonctions d'accesseur appropriées.

     

REMARQUE! Opacité et "fonctions d'accès" ne sont pas bons en eux-mêmes. La raison pour laquelle nous les avons pour des choses comme pte_t, etc., est qu’il existe vraiment zéro d’informations transférables et accessibles.

     

(b) Types d'entiers clairs, où l'abstraction aide à éviter toute confusion, qu'il s'agisse de "int" ou plus. ou "long".

     

u8 / u16 / u32 sont parfaitement typés, bien qu’ils entrent mieux dans la catégorie (d) qu’ici.

     

REMARQUE! Encore une fois - il doit y avoir une raison pour cela. Si quelque chose est "unsigned long", il n'y a aucune raison de le faire

typedef unsigned long myflags_t;
     

mais s'il existe une raison claire pour laquelle, dans certaines circonstances, il pourrait s'agir d'un "unsigned int" et sous d’autres configurations, il pourrait être "unsigned long", alors allez-y et utilisez une typedef.

     

(c) lorsque vous utilisez sparse pour créer littéralement un type nouveau pour la vérification du type.

     

(d) Nouveaux types identiques aux types C99 standard, dans certaines circonstances exceptionnelles.

     

Bien qu'il faille peu de temps aux yeux et au cerveau pour s'habituer aux types standard tels que 'uint32_t', certaines personnes s'opposent néanmoins à leur utilisation.

     

Par conséquent, les types 'u8 / u16 / u32 / u64 /' spécifiques à Linux et leurs équivalents signés identiques aux types standard sont autorisés - bien qu'ils ne soient pas obligatoires dans votre nouveau code.

     

Lorsque vous modifiez un code existant qui utilise déjà l'un ou l'autre des types de types, vous devez vous conformer aux choix existants dans ce code.

     

(e) Types sans danger pour une utilisation en espace utilisateur.

     

Dans certaines structures visibles par les utilisateurs, nous ne pouvons pas exiger de types C99 et ne pouvons pas utiliser le formulaire 'u32' ci-dessus. Ainsi, nous utilisons __u32 et des types similaires dans toutes les structures partagées avec l'espace utilisateur.

     

Peut-être existe-t-il d'autres cas également, mais la règle devrait être de ne JAMAIS JAMAIS utiliser un typedef, à moins que vous ne puissiez faire clairement correspondre l'une de ces règles.

     

En général, un pointeur ou une structure comportant des éléments auxquels on peut raisonnablement accéder directement ne devrait jamais être un typedef.

Il s’avère qu’il existe des avantages et des inconvénients. Le livre phare "Expert C Programming" est une source d’information utile. ( Chapitre 3 ). En bref, en C, vous disposez de plusieurs espaces de noms: tags, types, noms de membre et identificateurs . typedef introduit un alias pour un type et le localise dans l'espace de nom de la balise. À savoir,

typedef struct Tag{
...members...
}Type;

définit deux choses. Une balise dans l'espace de noms de balises et une autre type dans l'espace de noms de types. Vous pouvez donc faire les deux Type myType et struct Tag myTagType . Les déclarations telles que struct Type TypeType ou Balise typeTypeType sont illégales. De plus, dans une déclaration comme celle-ci:

typedef Type *Type_ptr;

nous définissons un pointeur sur notre type. Donc, si nous déclarons:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

then var1 , var2 et myTagType1 sont des pointeurs sur Type, mais myTagType2 n'est pas.

Dans le livre mentionné ci-dessus, il est mentionné que les structures de typage-définition ne sont pas très utiles car cela évite au programmeur d'écrire le mot struct. Cependant, j'ai une objection, comme beaucoup d'autres programmeurs C. Bien qu’il soit parfois obscurcissant certains noms (c’est pourquoi il n’est pas conseillé dans les bases de code volumineuses comme le noyau) d’implémenter le polymorphisme en C, cela aide beaucoup regardez ici pour plus de détails . Exemple:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

vous pouvez faire:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

Vous pouvez donc accéder à un membre externe ( flags ) par la structure interne ( MyPipe ) par le biais du casting. Pour moi, il est moins déroutant de transtyper le type entier que de faire (struct MyWriter_ *) s; chaque fois que vous souhaitez exécuter une telle fonctionnalité. Dans ces cas, un bref référencement est un gros problème, surtout si vous utilisez abondamment la technique dans votre code.

Enfin, le dernier aspect des types typedef ed est l’incapacité de les étendre, contrairement aux macros. Si, par exemple, vous avez:

#define X char[10] or
typedef char Y[10]

vous pouvez alors déclarer

unsigned X x; but not
unsigned Y y;

Nous ne nous soucions pas vraiment de cela pour les structures car cela ne s'applique pas aux spécificateurs de stockage ( volatile et const ).

Je ne pense pas que les déclarations en aval soient même possibles avec typedef. L'utilisation de struct, enum et union permet le transfert de déclarations lorsque les dépendances (en connaissance de cause) sont bidirectionnelles.

Style: L'utilisation de typedef en C ++ a beaucoup de sens. Cela peut être presque nécessaire lorsqu'il s'agit de modèles nécessitant des paramètres multiples et / ou variables. Le typedef aide à garder le nom exact.

Ce n’est pas le cas dans le langage de programmation C. L'utilisation de typedef ne sert le plus souvent à obscurcir l'utilisation de la structure de données. Étant donné que seul {struct (6), enum (4), union (5)} nombre de frappes de touche sont utilisés pour déclarer un type de données, l'aliasing de la structure n'a pratiquement aucune utilité. Ce type de données est-il une union ou une structure? L'utilisation de la simple déclaration non typdefed vous permet de savoir immédiatement de quel type il s'agit.

Remarquez comment Linux est écrit en évitant strictement cet aliasing non-sens typedef. Le résultat est un style minimaliste et épuré.

Commençons par les bases et poursuivons notre chemin.

Voici un exemple de définition de structure:

struct point
  {
    int x, y;
  };

Ici, le nom point est facultatif.

Une structure peut être déclarée lors de sa définition ou après.

Déclaration lors de la définition

struct point
  {
    int x, y;
  } first_point, second_point;

Déclaration après définition

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

Maintenant, notez soigneusement le dernier cas ci-dessus; vous devez écrire struct point pour déclarer des structures de ce type si vous décidez de créer ce type ultérieurement dans votre code.

Entrez typedef . Si vous avez l’intention de créer une nouvelle structure (la structure est un type de données personnalisé) ultérieurement dans votre programme en utilisant le même modèle, il peut être judicieux d’utiliser typedef lors de sa définition, car en tapant en avant.

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

Avertissement lors de la désignation de votre type personnalisé

Rien ne vous empêche d'utiliser le suffixe _t à la fin de votre nom de type personnalisé, mais le standard POSIX réserve l'utilisation du suffixe _t pour désigner les noms de types de bibliothèques standard.

le nom que vous donnez (éventuellement) à la structure est appelé nom de la balise et, comme il a été noté, n'est pas un type en soi. Pour obtenir le type nécessite le préfixe struct.

GTK + mis à part, je ne suis pas sûr que le nom de la variable utilisé soit aussi commun que le typedef du type struct, donc en C ++, il est reconnu et vous pouvez omettre le mot-clé struct et utiliser également le nom de la variable comme nom de type:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;

typedef ne fournira pas un ensemble dépendant de structures de données. Cela ne peut pas être fait avec typdef:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

Bien sûr, vous pouvez toujours ajouter:

typedef struct foo foo_t;
typedef struct bar bar_t;

Quel est exactement le but de cela?

A > une typdef facilite la signification et la documentation d'un programme en permettant la création de synonymes plus significatifs pour les types de données . De plus, ils aident à paramétrer un programme en fonction des problèmes de portabilité (K & amp; R, p. 147, programme C lang).

B > une structure définit un type . Structs permet de grouper facilement une collection de vars pour faciliter la manipulation (K & amp; Pg127, C prog lang.) En une seule unité

C > La définition d’une structure est expliquée dans A ci-dessus.

D > Pour moi, les structs sont des types personnalisés, des conteneurs, des collections, des espaces de noms ou des types complexes, alors qu'un typdef n'est qu'un moyen de créer plus de pseudonymes.

Il s'avère que dans C99, la typedef est requise. Il est désuet, mais beaucoup d’outils (comme HackRank) utilisent c99 comme implémentation en C pur. Et typedef est nécessaire ici.

Je ne dis pas qu'ils devraient changer (peut-être avoir deux options C) si l'exigence changeait, ceux d'entre nous qui étudions pour des interviews sur le site seraient SOL.

Dans le langage de programmation 'C', le mot clé 'typedef' est utilisé pour déclarer un nouveau nom pour un objet (struct, array, function..enum type). Par exemple, je vais utiliser un 'struct-s'. Dans 'C', nous déclarons souvent une 'structure' en dehors de la fonction 'principale'. Par exemple:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

Chaque fois que je décide d'utiliser un type de structure, j'ai besoin de ce mot-clé 'struct' quelque chose '' nom '.' typedef 'va tout simplement renommer ce type et je peux utiliser ce nouveau nom dans mon programme chaque fois que je le souhaite. Donc, notre code sera:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

Si vous avez un objet local (struct, tableau, valeur) qui sera utilisé dans tout votre programme, vous pouvez simplement lui donner un nom en utilisant un 'typedef'.

Du tout, en langage C, struct / union / enum sont des macro-instructions traitées par le préprocesseur en langage C (ne vous méprenez pas sur le préprocesseur qui traite "# include" et autre)

alors:

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

struct b est utilisé comme ceci:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

et ainsi, au moment de la compilation, il évolue sur la pile comme suit: b: int ai int i int j

C’est aussi pourquoi il est difficile d’avoir des structures auto-référentes, un préprocesseur C rond dans une boucle de clarification qui ne peut pas se terminer.

typedef est un spécificateur de type, cela signifie que seul le compilateur C le traite et il peut faire ce qu'il veut pour optimiser l'implémentation du code de l'assembleur. Il ne faut pas non plus dépenser un membre de type stupidement comme le processeur le fait avec des structures, mais utiliser un algorithme de construction de référence plus complexe, donc une construction comme:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

est autorisé et entièrement fonctionnel. Cette implémentation donne également accès à la conversion de type compilateur et supprime certains effets de bogue lorsque le thread d’exécution quitte le champ d’application des fonctions d’initialisation.

Cela signifie que les typedefs en C sont plus proches de la classe C ++ que de la structure lonely.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top