C structure 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 le fasse depuis des années, je n’ai jamais rencontré ceci:

struct device { }; struct spi_device { struct device dev; }; 

et 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 savoir quel est le but de cette définition. Il se trouve dans un fichier d’en-tête pour une application Linux de la carte, mais il est déconcerté par son utilisation. Des explications, des idées? Quelqu’un a vu ça avant (je suis sûr que certains d’entre vous ont :).

Merci! : bp:

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-declaration ne contient aucun membre nommé, que ce soit directement ou via une structure anonyme ou une union anonyme, le comportement est indé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 que cela peut être utilisé 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 y a un exemple de type de structure vide avec compilation conditionnelle dans la définition de l’alias de type spin_lock_t dans le kernel 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 de gagner de la place sans avoir à changer l’API des fonctions dans le cas de DEBUG_SPINLOCKS < 1 . Il permet également de définir des objects factices (de taille nulle) de type spinlock_t .

Un autre exemple dans le kernel 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

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

– Un type de structure décrit un ensemble non vide d’ objects membres alloués de manière séquentielle (et, dans certaines circonstances, un tableau incomplet), chacun d’entre eux ayant un nom éventuellement spécifié et éventuellement un type distinct.

J.2 Comportement non défini:

Le comportement n’est pas défini dans les circonstances suivantes:
….
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 une extension (aucun détail n’est donné sur quand / où doit-il être utilisé). Utiliser ceci dans n’importe quel programme le rendra spécifique au compilateur.

Une des raisons pour cela pour une bibliothèque est que les développeurs de bibliothèques ne veulent pas que vous sachiez ou que vous interfériez avec les composants internes de ces structures. Dans ces cas, ils peuvent fournir une version “interface” des structures spi_device/device (ce que vous pouvez voir) et une deuxième définition de type qui définit une autre version desdites structures à utiliser dans la bibliothèque avec les membres réels.

Etant donné que vous ne pouvez pas accéder aux membres de structure ou même créer des structures compatibles avec ce type avec cette approche (puisque même votre compilateur ne connaîtrait pas la taille réelle de cette structure), cela ne fonctionne que si la bibliothèque crée les structures pointeurs vers elle, et vous n’avez pas besoin de modifier les membres.

Si vous ajoutez une structure vide en tant que premier membre d’une autre structure, la structure vide peut servir d’interface marqueur, c’est-à-dire que lorsque vous lancez un pointeur vers cette structure externe vers un pointeur de la structure interne et que la dissortingbution réussit la structure externe est “marquée” comme quelque chose.

En outre, il pourrait simplement être une place pour le développement futur, pas à coup sûr. J’espère que cela t’aides

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 aux sous-programmes de la bibliothèque.

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