Использование анонимной структуры против именованной структуры с typedef

Когда одно из следующих утверждений должно использоваться поверх другого?

typedef struct Foo {
    int a;
} Bar;

а также

typedef struct {
    int a;
} Bar;

и использовать его как

Bar bar1 = { 5 };

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

Ответы

Ответ 1

Они в значительной степени эквивалентны. На самом деле, вы можете использовать одно и то же имя в обоих местах. И ты должен. Используйте то же имя, если только у вас нет веских причин не делать этого.

Одна из ситуаций, когда вы хотите использовать неанонимный объект, это когда вы хотите указатели на объект того же типа, как в связанном списке.

typedef struct Node {
    struct Node* next;
    int data;
} Node;

Одна альтернатива:

typedef struct Node Node;

struct Node {
    Node * next;
    int data;
};

По словам Линуса Торвальдса, вам следует избегать структур определения типов, если вы не хотите их скрывать. Из руководства по стилю кодирования ядра Linux:

Пожалуйста, не используйте такие вещи, как vps_t. Ошибочно использовать typedef для структур и указателей. Когда вы видите vps_t a; в источнике что это значит? Напротив, если это говорит struct virtual_container *a; вы можете сказать, что это такое.

Многие думают, что typedefs помогают читабельности. Не так. Они полезны только для:

а) полностью непрозрачные объекты (где typedef активно используется, чтобы скрыть, что это за объект).

...

В соответствии с этим, вы никогда не должны использовать анонимные структуры, а typedefs предназначены исключительно для интерфейсов. Так должно выглядеть так:

typedef struct Node {
    struct Node* next;
    int data;
} Node;

Но если вы действительно создаете интерфейс, вы должны разделить его на заголовочный файл и исходный файл. В этом случае поместите typedef в заголовочный файл и НЕ используйте typedef: ed вообще в исходном файле.

.c

struct Node {
    struct Node* next;
    int data;
} Node;

void insert(struct Node* head, int data) 
{
// Code
}    

.час

typedef struct Node Node;

void insert(Node* head, int data);

Принимая во внимание все вышесказанное, единственная допустимая ситуация для использования анонимной структуры - это если вы объявляете объект одновременно, как это:

struct {
    int data;
    float more_data;
} myObject; 

Ответ 2

Один из случаев, когда требуется первый, - это если вы создаете связанный список:

typedef struct list {
    int data;
    struct list *next;
} list;

ЬурейиЙ list не виден внутри определения структуры, так что вы должны использовать реальное имя структуры, чтобы создать указатель на него.

Если у вас нет такой структуры, вы можете использовать любую из них.

Однако не следует использовать имя тега, начинающееся с подчеркивания, то есть:

typedef struct _list {
    int data;
    struct list *next;
} list;

Потому что имена, начинающиеся с подчеркивания, зарезервированы реализацией.

Ответ 3

Это не имеет большого значения. Если вы используете помеченную форму, вы можете иметь указатели для struct Foo внутри struct Foo (AKA Bar)

typedef struct Foo{
  int a;
  struct Foo *foop;
} Bar;

но нет никакого способа сделать это со второй формой

typedef struct {
  int a;
  //Baz *p; not valid here since Baz isn't a typename yet
} Baz;

Некоторые базы кода предпочитают вообще не использовать typedef и просто пишут struct Foo каждый раз с ключевым словом struct.

Кроме того, с помощью первой формы вы можете ссылаться на тип либо через тег (struct Foo), либо с помощью typedefs (Bar или любого будущего/предыдущего typedef (вы можете выполнить typedef struct Foo PreviousTypedef; перед предоставлением определения).

Во второй форме, с другой стороны, вы можете использовать только Baz typedef и возможную будущую typedef (вы не можете forward- typedef структуру, так как она не имеет тега).

(Обратите внимание, что typedef самом деле не определяет типы в C. Часть struct optional_tag {/*...*/} делает. Скорее, typedef предоставляет псевдонимы типов (так что, возможно, он должен был называться typealias).)

Ответ 4

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

Фактический вопрос о том, когда использовать одну или другую, заключается в том, что вам нужно использовать первую форму, если вы хотите добавить указатель на ее собственный тип внутри нее, например, так:

typedef struct Foo { struct Foo* Child; ... } Foo;

Тем не менее, я бы предпочел сделать это с помощью typedef примерно так:

typedef struct Foo Foo;
struct Foo {Foo* Child;};

Ответ 5

Многие другие люди сосредотачиваются на самореферентном аспекте этого, но другая причина, чтобы избежать этого, заключается в том, что из-за отсутствия пространств имен в C. В некоторых кругах это стандартная практика - не использовать typedef для избежания struct спецификатора, а вместо этого ссылаются на структуры с полным спецификатором (например, void foo(struct Foo* foo_ptr)). Поэтому, если вы хотите сохранить такой стиль, у вас не будет возможности злоупотреблять анонимными структурами, вот так:

typedef struct {
  int a;
} Bar;

Bar bar1 = {5};

вместо этого всегда должно быть

struct Bar{
  int a;
};

struct Bar bar1 = {5};

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

Ответ 6

При создании непрозрачного типа данных, когда заголовок содержит только предварительное объявление struct и фактическое определение ее членов находится в исходном файле. Поскольку вы не можете пересылать объявление typedef вам нужно дать struct имя. Пример:

foo.h

typedef struct Foo_ Foo;

Foo.c

struct Foo_ {
    int a;
};

Также, когда у вас есть рекурсивная структура данных, такая как связанный список, который упоминали все остальные.