Использование анонимной структуры против именованной структуры с 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;
};
Также, когда у вас есть рекурсивная структура данных, такая как связанный список, который упоминали все остальные.