web-dev-qa-db-fra.com

Comment utiliser typedef et typedef enum en C?

Considérer:

#define MAXROW 20
#define MAXCOL 60
typedef State Grid[MAXROW+2] [MAXCOL+2]
typedef enum state {DEAD,ALIVE} State

Comment utiliser typedef et typedef enum en C? Que fait cette partie du code?

45
user39555
typedef enum state {DEAD,ALIVE} State;
|     | |                     | |   |^ terminating semicolon, required! 
|     | |   type specifier    | |   |
|     | |                     | ^^^^^  declarator (simple name)
|     | |                     |    
|     | ^^^^^^^^^^^^^^^^^^^^^^^  
|     |
^^^^^^^-- storage class specifier (in this case typedef)

Le mot clé typedef est un spécificateur de pseudo-classe de stockage. Syntaxiquement, il est utilisé au même endroit où un spécificateur de classe de stockage tel que extern ou static est utilisé. Cela n'a rien à voir avec le stockage. Cela signifie que la déclaration n'introduit pas l'existence de objets nommés, mais introduit plutôt des noms qui sont type aliases.

Après la déclaration ci-dessus, l'identifiant State devient un alias pour le type enum state {DEAD,ALIVE}. La déclaration fournit également ce type lui-même. Cependant, ce n'est pas typedef le faire. Toute déclaration dans laquelle enum state {DEAD,ALIVE} apparaît en tant que spécificateur de type introduit ce type dans la portée:

enum state {DEAD, ALIVE} stateVariable;

Si enum state a déjà été introduit, la typedef doit être écrite comme suit:

typedef enum state State;

sinon, la enum est en cours de redéfinition, ce qui est une erreur.

Comme d’autres déclarations (à l’exception des déclarations de paramètres de fonction), la déclaration typedef peut avoir plusieurs déclarateurs, séparés par une virgule. De plus, ils peuvent être des déclarants dérivés, pas seulement des noms simples:

typedef unsigned long ulong, *ulongptr;
|     | |           | |  1 | |   2   |
|     | |           | |    | ^^^^^^^^^--- "pointer to" declarator
|     | |           | ^^^^^^------------- simple declarator
|     | ^^^^^^^^^^^^^-------------------- specifier-qualifier list
^^^^^^^---------------------------------- storage class specifier

Cette typedef introduit deux noms de type ulong et ulongptr, basés sur le type unsigned long donné dans la liste des qualificateurs de qualificateurs. ulong est juste un alias direct pour ce type. ulongptr est déclaré en tant que pointeur sur unsigned long, grâce à la syntaxe *, qui, dans ce rôle, est une sorte d'opérateur de construction de type qui imite délibérément le * unaire pour le déréférencement de pointeur utilisé dans les expressions. En d'autres termes, ulongptr est un alias du type "pointeur sur unsigned long".

Alias ​​signifie que ulongptrn'est pas un type distinct de unsigned long *. Ce code est valide et ne nécessite aucun diagnostic:

unsigned long *p = 0;
ulongptr q = p;

Les variables q et p ont exactement le même type.

L'aliasing de typedef n'est pas textuel. Par exemple, si user_id_t est un nom typedef pour le type int, nous ne pouvons pas simplement faire ceci:

unsigned user_id_t uid;  // error! programmer hoped for "unsigned int uid". 

Il s'agit d'une liste de spécificateurs de type non valide, combinant unsigned avec un nom typedef. Ce qui précède peut être effectué à l'aide du préprocesseur C:

#define user_id_t int
unsigned user_id_t uid;

dans lequel user_id_t est étendu par macro au jeton int avant l'analyse syntaxique et la traduction. Bien que cela puisse sembler être un avantage, c'est un faux. éviter cela dans les nouveaux programmes.

Parmi les inconvénients, cela ne fonctionne pas bien pour les types dérivés:

 #define silly_macro int *

 silly_macro not, what, you, think;

Cette déclaration ne déclare pas what, you et think comme étant du type "pointeur sur int" car la macro-expansion est la suivante:

 int * not, what, you, think;

Le spécificateur de type est int et les déclarateurs sont *not, what, you et think. Donc, not a le type de pointeur attendu, mais pas les identificateurs restants.

Et cela représente probablement 99% de tout ce qui concerne typedef et le type aliasing en C.

90
Kaz

typedef définit un nouveau type de données. Pour que vous puissiez avoir:

typedef char* my_string;
typedef struct{
  int member1;
  int member2;
} my_struct;

Alors maintenant, vous pouvez déclarer des variables avec ces nouveaux types de données

my_string s;
my_struct x;

s = "welcome";
x.member1 = 10;

Pour enum, les choses sont un peu différentes - considérons les exemples suivants:

enum Ranks {FIRST, SECOND};
int main()
{
   int data = 20;
   if (data == FIRST)
   {
      //do something
   }
}

utiliser typedef enum crée un alias pour un type:

typedef enum Ranks {FIRST, SECOND} Order;
int main()
{
   Order data = (Order)20;  // Must cast to defined type to prevent error

   if (data == FIRST)
   {
      //do something
   }
}
22
Pandrei

Juste un complément: 

6.7.8 Définitions de type

Une déclaration typedef n'introduit pas un nouveau type, mais uniquement un synonyme pour le type ainsi spécifié.

open-std.org ISO/IEC 9899: 2017

Les personnes qui conviennent que typedef crée un nouveau type de données en C doivent utiliser trow dans la fonction free() et définir toutes les références à leurs noms sur NULL.

0
Undefined Behavior