web-dev-qa-db-fra.com

C struct vide - qu'est-ce que cela signifie/fait?

J'ai trouvé ce code dans un fichier d'en-tête pour un périphérique que je dois utiliser, et bien que je pratique le C depuis des années, je n'ai jamais rencontré ceci:

struct device {
};

struct spi_device {
    struct device dev;
};

et il a utilisé comme dans:

int spi_write_then_read(struct spi_device *spi, 
const unsigned char *txbuf, unsigned n_tx,
unsigned char *rxbuf, unsigned n_rx);

et aussi ici:

struct spi_device *spi = phy->spi;

où il est défini le même.

Je ne suis pas sûr de l'intérêt de cette définition. Il se trouve dans un fichier d’en-tête pour une application linux de la carte, mais je suis déconcerté par son utilisation. Des explications, des idées? Quelqu'un a déjà vu cela auparavant (je suis sûr que certains d'entre vous l'ont :)).

Merci! : bp:

46
Billy Pilgrim

Ce n'est pas C car les structures C doivent contenir au moins un membre nommé:

(C11, 6.7.2.1 Spécificateurs de structure et d'union p8) "Si la liste de struct-déclaration ne contient aucun membre nommé, directement ou via une structure anonyme ou une union anonyme, le comportement n'est pas défini."

mais une extension GNU C:

GCC permet à une structure C de ne pas avoir de membres:

struct empty {
};

La structure a la taille zéro

https://gcc.gnu.org/onlinedocs/gcc/Empty-Structures.html

Je ne sais pas quel est le but de cette construction dans votre exemple, mais en général, je pense qu'elle peut être utilisée comme une déclaration anticipée du type de structure. Notez qu'en C++, il est permis d'avoir une classe sans membre. 

Dans Linux 2.4, il existe un exemple de type de structure vide avec compilation conditionnelle dans la définition de l'alias de type spin_lock_t dans le noyau Linux 2.4 (dans include/linux/spinlock.h):

#if (DEBUG_SPINLOCKS < 1)

/* ... */

typedef struct { } spinlock_t;

#Elif (DEBUG_SPINLOCKS < 2)

/* ... */

typedef struct {
    volatile unsigned long lock;
} spinlock_t;

#else /* (DEBUG_SPINLOCKS >= 2) */

/* ... */

typedef struct {
    volatile unsigned long lock;
    volatile unsigned int babble;
    const char *module;
} spinlock_t;

#endif

Le but est d'économiser de l'espace sans avoir à changer les fonctions de l'API dans le cas où DEBUG_SPINLOCKS < 1. Cela permet également de définir des objets factices (taille zéro) de type spinlock_t.

Un autre exemple dans le noyau Linux (récent) d’un hack de structure vide utilisé avec la compilation conditionnelle dans include/linux/device.h:

struct acpi_dev_node {
#ifdef CONFIG_ACPI
    void *handle;
#endif
};

Voir la discussion avec Greg Kroah-Hartman pour ce dernier exemple ici:

https://lkml.org/lkml/2012/11/19/453

40
ouah

Ce n'est pas la norme C.
C11: 6.2.5-20:

- Un type de structure décrit un ensemble non vide alloué en séquence d'objets membre (et, dans certaines circonstances, un tableau incomplet), chacun d'eux ayant un nom éventuellement spécifié et un type éventuellement distinct. 

J.2 Comportement non défini:

Le comportement n'est pas défini dans les cas suivants:
....
- Une structure ou une union est définie sans aucun membre nommé (y compris ceux Spécifiés indirectement via des structures et des unions anonymes) (6.7.2.1). 

GCC l'utilise comme extension (il n'y a pas plus de détails sur quand/où il devrait être utilisé). Son utilisation dans n'importe quel programme le rendra spécifique au compilateur.

22
haccks

Une raison de faire cela pour une bibliothèque est que les développeurs de la bibliothèque ne veulent pas que vous sachiez ou que vous interfériez avec les éléments internes de cette structure. Dans ce cas, ils peuvent fournir une version "interface" de la structure spi_device/device (ce que vous pouvez voir) et une deuxième définition de type définissant une autre version de cette structure à utiliser dans la bibliothèque avec les membres réels.

Puisque vous ne pouvez pas accéder aux membres de la structure ni même créer vous-même des structures compatibles de ce type avec cette approche (même votre compilateur ne connaît pas la taille réelle de cette structure), cela ne fonctionne que si la bibliothèque crée elle-même les structures, vous ne le transmettez jamais. pointeurs, et n’a pas besoin de modifier les membres.

3
Dreamer

Si vous ajoutez une structure vide en tant que premier membre d'une autre structure, la structure vide Peut servir "d'interface de marqueur", c'est-à-dire lorsque vous transposez un pointeur sur cette structure Outer en un pointeur de la structure interne et la distribution réussit vous savez, que la structure externe est "marquée" comme quelque chose.

En outre, il se peut que ce ne soit qu'un remplaçant pour un développement futur, pas certain. J'espère que cela t'aides

2
Intrepid Traveller

Ceci est valide C

struct empty;
struct empty *empty;

et facilite l'utilisation des adresses des régions opaques de la mémoire.

Ces adresses sont généralement obtenues et transmises à des sous-routines de bibliothèque.

Par exemple, quelque chose comme ceci est fait dans stdio.h

0
J M D