C пустая структура - что это значит/делать?

Я нашел этот код в файле заголовка для устройства, которое мне нужно использовать, и хотя я занимаюсь C годами, я никогда не сталкивался с этим:

struct device {
};

struct spi_device {
    struct device dev;
};

и используется как в:

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

а также здесь:

struct spi_device *spi = phy->spi;

где он определяется тем же самым.

Я не уверен, в чем дело с этим определением. Он находится в файле заголовка для приложения linux на доске, но я не понимаю его использование. Любые объяснения, идеи? Кто-нибудь видел это раньше (я уверен, что некоторые из вас:).

Спасибо! : П.н.:

Ответ 1

Это не C, поскольку структуры C должны содержать хотя бы один именованный элемент:

(C11, 6.7.2.1 Спецификаторы структуры и объединения p8) "Если список struct-declaration-list не содержит каких-либо именованных членов, напрямую или через анонимную структуру или анонимный союз, поведение undefined."

но расширение GNU C:

GCC разрешает структуре C не иметь членов:

struct empty {
};

Структура имеет нулевой размер

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

Я не знаю, какова цель этой конструкции в вашем примере, но в целом я думаю, что ее можно использовать в качестве прямого объявления типа структуры. Обратите внимание, что в С++ разрешено иметь класс без члена.

В Linux 2.4 приведен пример пустого типа структуры с условной компиляцией в определении псевдонима типа t21 в ядре Linux 2.4 (в 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

Цель состоит в том, чтобы сохранить некоторое пространство без необходимости изменять функции API в случае DEBUG_SPINLOCKS < 1. Он также позволяет определять объекты типа "манекен" (нулевой размер) типа spinlock_t.

Еще один пример в (недавнем) ядре Linux пустого хакера структуры, используемого с условной компиляцией в include/linux/device.h:

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

См. обсуждение с Грегом Кроа-Хартманом для этого последнего примера здесь:

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

Ответ 2

Это не стандарт C.
C11: 6.2.5-20:

- Тип структуры описывает последовательно выделенный непустой набор объектов (и, в некоторых случаях, неполный массив), каждый из которых имеет необязательно указанное имя и, возможно, отдельный тип.

J.2 Undefined поведение:

Поведение Undefined в следующих случаях:
....
- Структура или объединение определяется без каких-либо именованных членов (включая те, указанных косвенно через анонимные структуры и союзы) (6.7.2.1).

GCC использует его как расширение (здесь не указано более подробное описание того, когда/где его следует использовать). Использование этого в любой программе сделает его специфичным для компилятора.

Ответ 3

Одна из причин может сделать это для библиотеки, так это то, что разработчики библиотеки не хотят, чтобы вы знали или мешали внутренним структурам этой структуры. В этих случаях они могут предоставить "интерфейсную" версию структур spi_device/device (что вы можете видеть) и иметь определение второго типа, которое определяет другую версию указанных структур для использования внутри библиотеки с фактическими членами.

Поскольку вы не можете получить доступ к членам структуры или даже создать совместимые структуры этого типа самостоятельно с этим подходом (так как даже ваш компилятор не знает размер фактического размера этой структуры), это работает только в том случае, если сама библиотека создает структуры только когда-либо передает вам указатели на него, и вам не нужно изменять какие-либо члены.

Ответ 4

Если вы добавляете пустую структуру в качестве первого члена другой структуры, пустой struct может служить "интерфейсом маркера", т.е. когда вы накладываете указатель на этот внешняя структура - указатель внутренней структуры, и литье успешно вы знаете что внешняя структура "помечена" как нечто.

Кроме того, это может быть просто место для будущего развития, а не наверняка. Надеюсь, что это поможет

Ответ 5

Это действительно C

struct empty;
struct empty *empty;

и облегчает использование адресов непрозрачных областей памяти.

Такие адреса обычно получаются и передаются в библиотечные подпрограммы.

Например, что-то подобное делается в stdio.h

Ответ 6

Я знаю, что это древняя нить, и я не хочу обходить некро-биение, но я сделал свои собственные эксперименты с пустыми структурами внутри связанных списков, я считаю, что мои исследования заслуживают упоминания, поскольку этот поток строго связан на что может означать/сделать пустую структуру.

Бланк-структуры отлично подходят для использования в GCC. Из того, что я нашел, до тех пор, пока вы malloc-структуры указателя, вы действительно выделите соответствующее пространство для указателя внутри этой структуры. Несмотря на то, что он имеет пустой typedef, т.е. Нет членов, вы все еще объявляете переменную-указатель внутри структуры. Если вы NULL объявите их, то ничего не будет работать.

Действительно, несмотря на это, вы можете использовать обычный связанный список. Это действительно ничего особенного, всего лишь немного кода, который я не мог не написать.

Код выглядит следующим образом:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/* 
 * File:   main.c
 * Author: access
 *
 * Created on October 2, 2016, 11:23 PM
 */

#include <stdio.h>
#include <stdlib.h>

typedef struct {

} blankptr;

typedef struct {

    struct blankptr *blankptr;
    struct node *dataptr;

} ptrstruct;

typedef struct {

    int val;
    float x;
    float y;
    char test;

} node;



/*
 * 
 */
int main(int argc, char** argv) {

ptrstruct *ptr0 = malloc(sizeof(*ptr0));
ptrstruct *ptr1 = malloc(sizeof(*ptr1));
ptrstruct *ptr2 = malloc(sizeof(*ptr2));
ptrstruct *ptr3 = malloc(sizeof(*ptr3));


node *data0 = malloc(sizeof(*data0));
node *data1 = malloc(sizeof(*data1));
node *data2 = malloc(sizeof(*data2));
node *data3 = malloc(sizeof(*data3));


ptr0->blankptr = ptr1;
ptr0->dataptr = data1;

printf("ptr0->blankptr = %p \n", ptr0->blankptr);
printf("ptr0->dataptr = %p \n", ptr0->dataptr);


ptr1->blankptr = ptr2;
ptr1->dataptr = data2;

printf("ptr1->blankptr = %p \n", ptr1->blankptr);
printf("ptr1->dataptr = %p \n", ptr1->dataptr);

ptr2->blankptr = ptr3;
ptr2->dataptr = data3;

printf("ptr2->blankptr = %p \n", ptr2->blankptr);
printf("ptr2->dataptr = %p \n", ptr2->dataptr);

    return (EXIT_SUCCESS);
}