web-dev-qa-db-fra.com

C: typedef nom de la structure {...}; VS typedef struct {...} nom;

Comme le titre l'indique, j'ai ce code:

typedef struct Book{
    int id;
    char title[256];
    char summary[2048];
    int numberOfAuthors;
    struct Author *authors;
};


typedef struct Author{
    char firstName[56];
    char lastName[56];
};


typedef struct Books{
    struct Book *arr;
    int numberOfBooks;
};

Je reçois ces erreurs de gcc:

bookstore.c:8:2: error: unknown type name ‘Author’
bookstore.c:9:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:15:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:21:2: error: unknown type name ‘Book’
bookstore.c:23:1: warning: useless storage class specifier in empty declaration [enabled by default]

Si je change les typedefs comme ceci:

typedef struct{
    char firstName[56];
    char lastName[56];
} Author;

Alors aucun avertissement ni aucune erreur ne se produisent. Après avoir recherché http://www.Amazon.com/C-Programming-Language-2nd-Edition/dp/0131103628 et quelques heures à googler, je ne vois pas pourquoi la première mise en œuvre ne fonctionnerait pas.

32
Alek Sobczyk

Il y a plusieurs choses qui se passent ici. Premièrement, comme d'autres l'ont dit, la plainte du compilateur à propos d'un type inconnu peut être due au fait que vous devez définir les types avant de les utiliser. Le plus important est de comprendre la syntaxe de 3 choses: (1) la définition de la structure, (2) la déclaration de la structure et (3) la typedef.

Lors de la définition d'une structure, celle-ci peut être nommée ou non-nommée (si elle n'est pas nommée, elle doit alors être utilisée immédiatement (expliquera ce que cela signifie plus bas)).

struct Name {
   ...
};

Ceci définit un type appelé "nom de structure" qui peut ensuite être utilisé pour déclarer une variable de structure:

struct Name myNameStruct;

Ceci déclare une variable appelée myNameStruct qui est une structure de type struct Name.

Vous pouvez également définir une structure et déclarer une variable de structure en même temps:

struct Name {
   ...
} myNameStruct;

Comme auparavant, ceci déclare une variable appelée myNameStruct qui est une structure de type struct Name ... Mais elle le fait en même temps, elle définit le type struct Name.
Le type peut être utilisé à nouveau pour déclarer une autre variable:

struct Name myOtherNameStruct;

Maintenant, typedef est juste un moyen d'aliaser un type avec un nom spécifique:

typedef OldTypeName NewTypeName;

Compte tenu de la typedef ci-dessus, chaque fois que vous utilisez NewTypeName, cela revient à utiliser OldTypeName. Dans le langage de programmation C, cela est particulièrement utile avec les structures, car il vous permet de ne pas utiliser le mot "structure" lors de la déclaration de variables de ce type et de traiter le nom de la structure simplement comme un type ( comme nous le faisons en C++). Voici un exemple qui définit d'abord la structure, puis typedefs la structure:

struct Name {
   ...
};

typedef struct Name Name_t;

Dans ce qui précède, OldTypeName est struct Name et NewTypeName est Name_t. Alors maintenant, pour déclarer une variable de type struct Name, au lieu d'écrire:

struct Name myNameStruct;

Je peux écrire simplement:

Name_t myNameStruct;

NOTE AUSSI, le typedef PEUT ÊTRE COMBINÉ à la définition de struct, et voici ce que vous faites dans votre code:

typedef struct {
   ...
} Name_t;

Cela peut aussi être fait en nommant la structure, mais c'est superflu:

typedef struct Name {
   ...
} Name_t;

NOTE WELL: Dans la syntaxe ci-dessus, puisque vous avez commencé avec "typedef", l'instruction entière est une instruction typedef, dans laquelle OldTypeName se trouve être une définition de structure. Par conséquent, le compilateur interprète le nom qui arrive après l’accolade droite} comme étant le NewTypeName ... c’est ET NON le nom de la variable (comme dans la syntaxe sans typedef, auquel cas vous serait de définir la structure et de déclarer une variable de structure en même temps). 

En outre, si vous indiquez typedef, mais laissez le Name_t à la fin, vous avez effectivement créé une instruction INCOMPLETE typedef , car le compilateur considère tout ce qui se trouve dans "struct Name { ... }" comme étant OldTypeName, et vous ne fournissez pas de NewTypeName pour le typedef. C'est pourquoi le compilateur n'est pas satisfait du code tel que vous l'avez écrit (bien que les messages du compilateur soient plutôt cryptiques, car vous ne savez pas exactement ce que vous avez mal fait).

Maintenant, comme je l’ai noté ci-dessus, si vous ne nommez pas le type de structure au moment où vous le définissez, vous devez l’utiliser immédiatement pour déclarer une variable:

struct {
   ...
} myNameStruct;  // declares myNameStruct as a variable with this struct
                 // definition, but the definition cannot be re-used.

Ou vous pouvez utiliser un type struct non nommé dans un typedef:

typedef struct {
   ...
} Name_t;

Cette syntaxe finale est ce que vous avez réellement fait quand vous avez écrit:

typedef struct{
   char firstName[56];
   char lastName[56];
} Author;

Et le compilateur était heureux. HTH.

Concernant le commentaire/la question sur le suffixe _t:

Le suffixe _t est une convention, pour indiquer aux personnes qui lisent le code que le nom symbolique avec le _t est un nom de type (par opposition à un nom de variable). Le compilateur n'analyse pas et n'est pas au courant du _t. 

Les bibliothèques standard C89, et en particulier C99, définissaient de nombreux types ET CHOISI D'UTILISER le _t pour les noms de ces types. Par exemple, le standard C89 définit wchar_t, off_t, ptrdiff_t. La norme C99 définit de nombreux types supplémentaires, tels que uintptr_t, intmax_t, int8_t, uint_least16_t, uint_fast32_t, etc. Mais _t n'est pas réservé, ni spécialement analysé, ni remarqué par le compilateur, c'est simplement une convention qu'il convient de suivre. lorsque vous définissez de nouveaux types (via typedef) en C. En C++, de nombreuses personnes utilisent la convention pour démarrer les noms de type avec une majuscule, par exemple, MyNewType (par opposition à la convention C my_new_type_t). HTH

74
Daniel Goldfarb

La syntaxe est de typedef est la suivante:

typedef old_type new_type

Lors de votre première tentative, vous avez défini le type struct Book et not Book. En d'autres termes, votre type de données s'appelle struct Book et non pas Book.

Dans le second formulaire, vous utilisiez la syntaxe correcte de typedef. Le compilateur reconnaît donc le type appelé Book.

5
Bechir

Je pense que ça va vous aider à comprendre . http://www.tutorialspoint.com/cprogramming/c_typedef.htm

bookstore.c:8:2: error: unknown type name ‘Author’
bookstore.c:21:2: error: unknown type name ‘Book’

Celles-ci sont produites parce que vous devez les définir avant de les utiliser. Déplacez la structure "Auteur" et "Livres" au-dessus de la structure "Livre". Cela va le résoudre.

Aussi, l'avertissement que vous obtenez explique pourquoi il y a un problème, le compilateur identifie "typedef struct Author" comme non nécessaire car vous n'êtes pas correctement typé la structure, il est donc inutile que le compilateur "lise".

Puisque vous savez déjà que la réponse devrait être sous cette forme

typedef struct {
 ...
 ... 
 ...
} struct-name;

s'en tenir à cela.

0
aiked0

Il vous suffit de définir Auteur avant de définir Book.

Vous utilisez Author dans Book, il doit donc être défini avant.

0
EoiFirst