Почему имена typedef дважды используются в описании структуры в C?

При исследовании очередей в C я столкнулся с примером, подобным приведенному ниже. Почему структура называется как в начале фигурных скобок, так и после? Почему тип структуры используется снова внутри структуры при добавлении элемента того же типа? Являются ли эти вещи лишними или есть точка?

typedef void* vpoint_t;

typedef struct queue_item_t{
  vpoint_t void_item;
  struct queue_item_t* next;
} queue_item_t;

Ответ 1

typedef struct queue_item_t {     // 1
  vpoint_t void_item;
  struct queue_item_t* next;      // 2
} queue_item_t;                   // 3

Прежде всего, обратите внимание, что весь этот оператор определяет typedef.

3) Говорят, что новый тип, который мы находимся в процессе определения (через typedef), будет называться queue_item_t.

1) Имя структуры (которому присваивается новое имя, по мере продвижения), называется struct queue_item_t. Это полное имя, включая struct спереди.

2) Поскольку новый тип еще не существует (помните, что мы все еще находимся в процессе его определения), мы должны использовать единственное имя, которое оно имеет до сих пор, struct queue_item_t, от 1).


Обратите внимание, что вы можете иметь анонимные определения struct, которые позволяют вам опустить имя из 1). Простой пример:

typedef struct {
   int x, y, z;
} vector3;

В вашем примере, однако, поскольку нам нужна структура, чтобы иметь возможность ссылаться на себя, указатель next должен иметь уже определенный тип. Мы можем это сделать, перейдя по объявлению структуры, набрав ее, затем определив структуру, используя тип typedef d для next:

struct _queue_item;                           // 4

typedef struct _queue_item queue_item_t;      // 5

struct _queue_item {                          // 6
  vpoint_t void_item;
  queue_item_t* next;                         // 7
}

4) Объявите, что struct _queue_item существует, но еще не предоставил для него определения.

5) Typedef queue_item_t будет таким же, как struct _queue_item.

6) Теперь дайте определение структуры...

7)... используя наш typedef 'd queue_item_t.


Все, что было сказано... По-моему, , пожалуйста, не используйте typedefs для structs.

struct queue_item {
    void *data;
    struct queue_item *next;
}

является простым и полным. Вы можете ввести шесть дополнительных символов.

Из Стиль кодирования ядра Linux:

Глава 5: Typedefs

Пожалуйста, не используйте такие вещи, как "vps_t".     Ошибочно использовать typedef для структур и указателей. Когда вы видите

  vps_t a;

в источнике, что это значит? Напротив, если он говорит

  struct virtual_container *a;

вы действительно можете сказать, что такое "а".

Есть исключения, о которых вы можете прочитать.


Некоторые недавние связанные вопросы:

Ответ 2

Немного измените объявление, чтобы упростить обсуждение:

typedef struct queue_item {
  vpoint_t void_item;
  struct queue_item* next;
} QueueItemType;

C поддерживает несколько разных пространств имен; одно пространство имен зарезервировано для имен тегов в объединениях, структурах и типах перечислений. В этом случае имя тега queue_item. Другое пространство имен зарезервировано для регулярных идентификаторов, включая имена typedef, такие как QueueItemType.

Элемент next используется для указания на другой экземпляр типа struct queue_item (т.е. следующий элемент в очереди). Он объявлен как указатель на struct queue_item по двум причинам:

  • Тип структуры не может содержать экземпляр самого себя; во-первых, тип должен быть бесконечно большим (struct queue_item содержит член next, который является struct queue_item, который содержит член next, который является struct queue_item, который содержит член next, ad infinitum);

  • Определение типа структуры не завершено до закрытия }, и вы не можете объявить экземпляр неполного типа. Однако вы можете объявить указатель на неполный тип, который мы делаем ниже:

    struct queue_item * next;

Почему бы не использовать QueueItemType *next; вместо struct queue_item *next? Опять же, определение типа структуры не является полным в точке next; имя typedef QueueItemType еще не существует. Тем не менее, имя тега queue_item уже видно компилятору, поэтому мы можем объявлять указатели, используя тип struct queue_item.

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

Ответ 3

Во-первых, я предлагаю НИКОГДА не использовать имя структуры и идентичное имя типа. Используйте typedef struct QUEUE_ITEM {...} queue_item_t;

Что касается вопроса: если вы хотите создать "рекурсивную структуру данных", то есть структуру данных, которая имеет указатели на экземпляры самого себя, тогда вы должны быть способны сообщить компилятору "Это поле является указателем на один из нас. Вы еще не знаете, каково это, но я все еще определяю его, поэтому просто зарезервируйте место для указателя". Для этого вы объявляете

struct T {
    ...
    struct T *ptr;
    ....
};

С окончательным } queue_item_t; вы создадите новое имя для структуры.

Ответ 4

"struct foo {...}" - это одно. Он определяет структуру, вам нужно набрать "struct foo" в любом месте, где вы его используете.

"typedef... foo" определяет новый тип, поэтому вы просто набираете "foo", где вы его используете.

"typedef struct foo {...} foo" - это идиома, поэтому вы можете использовать оба варианта, скорее всего, просто "foo", чтобы сохранить нажатия клавиш и визуальное загрязнение.