Les définitions de structure doivent-elles aller dans un fichier .h ou .c?

102

J'ai vu à la fois des définitions complètes de structs dans les en-têtes et juste des déclarations - y a-t-il un avantage à une méthode par rapport à l'autre?

Si cela fait une différence, je tape généralement une structure comme celle-ci dans le .h

typedef struct s s_t;

Éditer

Pour être clair, les options sont la déclaration dans le fichier d'en-tête et la définition dans la classe, ou à la fois la déclaration et la définition dans le fichier d'en-tête. Les deux devraient aboutir à la même utilisabilité, même si on est par liaison, n'est-ce pas?


Je vois de nombreux presque doublons, par exemple ici mais pas de correspondances exactes. Veuillez me corriger si je me trompe à cet égard.

Aaron Yodaiken
la source
2
Voulez-vous une structure opaque ou non opaque?
4
Remarque: les identifiants avec _tsont réservés par POSIX, c'est donc généralement une mauvaise idée. Vous pouvez simplement le faire typedef struct toto toto.
Jens Gustedt
J'ai vu beaucoup d' _tautres endroits utiliser (par exemple, lighttp, linux) ... et je préfixe les choses avec projident_ donc, cela ne devrait pas être un problème, n'est-ce pas?
Aaron Yodaiken
Et @WTP, je pense que le non-opaque est généralement considéré comme meilleur et plus Cish, non (avec l' FILEexemple, etc.). Donc, non opaque.
Aaron Yodaiken
Si c'est une structure non opaque, elle doit aller dans un fichier d'en-tête, ou votre code n'est pas DRY (ne vous répétez pas).

Réponses:

107

Les structures privées de ce fichier doivent aller dans le fichier .c, avec une déclaration dans le fichier .h si elles sont utilisées par des fonctions du fichier .h.

Les structures publiques doivent aller dans le fichier .h.

τεκ
la source
4
Je pense que je suis plus d'accord avec cette réponse. Il ne s'agit pas d'utiliser la structure via d'autres fichiers .c ou non, mais de savoir si la structure doit être considérée comme publique (et donc accessible) ou non.
c00kiemon5ter
@ τεκ Voulez-vous dire globalet localvisibilité? publicn'a pas de sens dans une structure. Toutes les structures sont publiques par défaut.
BugShotGG
3
@Geo Papas Ceci est une question sur C. publicn'est pas un mot-clé en C. Si vous regardez la réponse de Matthew Slattery ci-dessous, vous pouvez voir comment l'utilisation d'une seule déclaration directe dans l'en-tête provoque une erreur du compilateur lorsque l'utilisateur tente d'utiliser les membres d'un structure privée (opaque).
τεκ
68

Les deux devraient aboutir à la même utilisabilité, même si on est par liaison, n'est-ce pas?

Non, pas lorsque vous considérez d'autres fichiers .c incluant le même en-tête. Si la définition de la structure n'est pas visible par le compilateur, les détails de cette définition ne peuvent pas être utilisés. Une déclaration sans définition (par exemple juste struct s;) fait échouer le compilateur si quelque chose tente de regarder à l'intérieur struct s, tout en lui permettant par exemple de compiler struct s *foo;(tant quefoo n'est pas déréférencé ultérieurement).

Comparez ces versions de api.het api.c:

Definition in header:                 Definition in implementation:
+---------------------------------+   +---------------------------------+
| struct s {                      |   | struct s;                       |
|     int internal;               |   |                                 |
|     int other_stuff;            |   | extern void                     |
| };                              |   | api_func(struct s *foo, int x); |
|                                 |   +---------------------------------+
| extern void                     |   +---------------------------------+
| api_func(struct s *foo, int x); |   | #include "api.h"                |
+---------------------------------+   |                                 |
+---------------------------------+   | struct s {                      |
| #include "api.h"                |   |     int internal;               |
|                                 |   |     int other_stuff;            |
| void                            |   | };                              |
| api_func(struct s *foo, int x)  |   |                                 |
| {                               |   | void                            |
|     foo->internal = x;          |   | api_func(struct s *foo, int x)  |
| }                               |   | {                               |
+---------------------------------+   |     foo->internal = x;          |
                                      | }                               |
                                      +---------------------------------+

Ce client de l'API fonctionne avec les deux versions:

#include "api.h"

void good(struct s *foo)
{
    api_func(foo, 123);
}

Celui-ci fouille dans les détails de l'implémentation:

#include "api.h"

void bad(struct s *foo)
{
    foo->internal = 123;
}

qui fonctionnera avec la version "définition en entête", mais pas avec la version "définition en implémentation", car dans ce dernier cas le compilateur n'a pas de visibilité sur la mise en page de la structure:

$ gcc -Wall -c bad.c
bad.c: In function 'bad':
bad.c:5: error: dereferencing pointer to incomplete type
$

Ainsi, la version «définition dans l'implémentation» protège contre l'utilisation abusive accidentelle ou délibérée des détails d'implémentation privée.

Matthew Slattery
la source
3
vous voulez juste savoir comment vous avez créé ces fenêtres de code et avez toujours le code mis en évidence à l'intérieur ... manuellement? Cet OP semble être parti en utilisant stackoverflow: '(Quelqu'un d'autre peut-il me dire ....
Mahesha999
Bel exemple! Merci!
Victor Haine
Merci pour cet exemple! dereferencing pointer to incomplete typeétait exactement mon cas!
Timur Fayzrakhmanov
Je voudrais juste ajouter que toutes les structures accessibles au public ne sont pas mauvaises: vous pourriez par exemple vouloir permettre à l'utilisateur de votre API de remplir des données et de les envoyer.
Alexander Torstling
@ Mahesha999, il n'y a pas de magie là-bas. SO met en évidence le code même si vous y mettez des déchets. Remarquez qu'il essaie de mettre en évidence la sortie de la ligne de commande plus tard dans l'article.
Ailier Sendon le
8

Si la structure doit être utilisée par d'autres unités de compilation (fichiers .c), placez-la dans le fichier d'en-tête afin de pouvoir inclure ce fichier d'en-tête partout où il est nécessaire.

Si la structure n'est utilisée que dans une seule unité de compilation (fichier .c), vous la placez dans ce fichier .c.

nos
la source
3

Le fait est que le placer dans un fichier d'en-tête vous permet d'utiliser la structure (ou toute autre définition) à partir de plusieurs fichiers source, simplement en incluant ce fichier d'en-tête.

Mais si vous êtes sûr qu'il ne sera utilisé qu'à partir d'un seul fichier source, cela ne fait vraiment aucune différence.

Jonathan Wood
la source
-4

En général, je ne pense pas que cela fasse une énorme différence si vous les mettez dans l'en-tête ou dans les fichiers source. Toutefois, si vous avez besoin d'accéder aux membres d'une structure à partir de plusieurs fichiers source, il est plus facile de placer la structure dans un fichier d'en-tête et de l'inclure à partir de tout autre fichier où la structure est nécessaire.

Frxstrem
la source
8
-1: si vous vous souciez d'une bonne ingénierie logicielle (abstraction, modularité, etc.), il importe en fait où vous mettez la définition de la structure
Paul R