tag descripcion c struct

descripcion - C estructura vacía: ¿qué significa esto/hacer?



meta title y meta descripcion (5)

Esto es válido C

struct empty; struct empty *empty;

y facilita el uso de direcciones de regiones opacas de la memoria.

Tales direcciones usualmente se obtienen y pasan a las subrutinas de la biblioteca.

Por ejemplo, algo así se hace en stdio.h

Encontré este código en un archivo de cabecera para un dispositivo que necesito usar, y aunque he estado haciendo C por años, nunca me he encontrado con esto:

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

y lo usó como en:

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

y también aquí:

struct spi_device *spi = phy->spi;

donde se define lo mismo.

No estoy seguro de cuál es el punto con esta definición. Está en un archivo de encabezado para una aplicación de Linux de la placa, pero estoy desconcertado por su uso. ¿Alguna explicación, ideas? Alguien ha visto esto antes (estoy seguro de que algunos de ustedes lo han hecho :).

¡Gracias! : bp:


Esto no es C ya que las estructuras C deben contener al menos un miembro nombrado:

(C11, 6.7.2.1 Estructura y especificadores de unión p8) "Si struct-declaration-list no contiene ningún miembro con nombre, ya sea directamente o a través de una estructura anónima o unión anónima, el comportamiento no está definido".

pero una extensión GNU C:

GCC permite que una estructura C no tenga miembros:

struct empty { };

La estructura tiene tamaño cero

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

No sé cuál es el propósito de este constructo en su ejemplo, pero, en general, creo que puede usarse como una declaración directa del tipo de estructura. Tenga en cuenta que en C ++ se permite tener una clase sin miembro.

En Linux 2.4 hay un ejemplo de un tipo de estructura vacía con compilación condicional en la definición de spin_lock_t tipo alias en Linux kernel 2.4 (en 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

El objetivo es ahorrar algo de espacio sin tener que cambiar las funciones de la API en caso de DEBUG_SPINLOCKS < 1 . También permite definir objetos ficticios (de tamaño cero) de tipo spinlock_t .

Otro ejemplo en el kernel de Linux (reciente) de un hack de estructura vacío usado con compilación condicional en include / linux / device.h:

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

Vea la discusión con Greg Kroah-Hartman para este último ejemplo aquí:

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


Esto no es estándar C.
C11: 6.2.5-20:

- Un tipo de estructura describe un conjunto no vacío distribuido secuencialmente de objetos miembros (y, en ciertas circunstancias, una matriz incompleta), cada uno de los cuales tiene un nombre opcionalmente especificado y posiblemente un tipo distinto.

J.2 Comportamiento indefinido:

El comportamiento no está definido en las siguientes circunstancias:
....
- Una estructura o unión se define sin miembros nombrados (incluidos los especificados indirectamente a través de estructuras y uniones anónimas) (6.7.2.1).

GCC lo usa como una https://gcc.gnu.org/onlinedocs/gcc/Empty-Structures.html (no se da más detalles sobre cuándo / dónde debería usarse). Usar esto en cualquier programa lo hará específico del compilador.


Si agrega una estructura vacía como el primer miembro de otra estructura, la estructura vacía puede servir como una "interfaz de marcador", es decir, cuando lanza un puntero a esa estructura externa a un puntero de la estructura interna y el reparto tiene éxito, usted sabe que la estructura externa está "marcada" como algo.

También podría ser un marcador de posición para el desarrollo futuro, no estoy seguro. Espero que esto ayude


Una razón para hacer esto para una biblioteca es que los desarrolladores de la biblioteca no quieren que usted conozca o interfiera con el funcionamiento interno de estas estructuras. En estos casos pueden proporcionar una versión de "interfaz" de las estructuras spi_device/device (que es lo que puede ver) y tienen una segunda definición de tipo que define otra versión de dichas estructuras para su uso dentro de la biblioteca con los miembros reales.

Como no puede acceder a los miembros de la estructura o incluso puede crear estructuras compatibles de ese tipo con ese enfoque (ya que incluso su compilador no conocería el tamaño del tamaño real de esta estructura), esto solo funciona si la biblioteca crea las estructuras, solo le pasa apunta a él, y no necesita que modifique ningún miembro.