Подтвердить что ты не робот

Почему "typdef struct {struct S * s;} S;" содержащий указатель на компиляцию того же типа?

Я пытаюсь typedef a struct, который содержит указатель на другой тип того же типа.

То, что я думал, будет лучшей версией:

typedef struct Element
{
    char value;
    struct Element *next;
} Element;

Почему этот вариант также компилируется + выполняется?:

typedef struct
{
    char value;
    struct Element *next;
} Element;

Чтобы описать первое, я бы сказал: "Name struct Element Element now", а второй как: "Возьмите этот анонимный struct и назовите его Element"

Но почему я могу объявить struct Element (внутри структуры) во втором случае?

(Работает в GCC и MSVC)

4b9b3361

Ответ 1

В первом случае ваша структура имеет два эквивалентных имени: struct Element (где Element - тег структуры) и Element (где Element - typedef, псевдоним для существующего типа).

Во втором случае вы просто не определили тег для структуры. Обычно это было бы совершенно корректно, но здесь вы ссылаетесь на несуществующий тип struct Element в объявлении члена next.

В этом контексте struct Element является неполным типом. Вы не можете объявлять объекты неполных типов, но вы можете объявлять им указатели.

Объявление

typedef struct
{
    char value;
    struct Element *next;
} Element;

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

Ваше второе объявление - одно из множества вещей, которые не имеют смысла, но по-прежнему являются законными.

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

Обратите внимание, что вам нужно обратиться к типу struct Element, а не Element, в самом определении, так как имя typedef Element еще не отображается.

Тот факт, что тег struct и typedef имеют одно и то же имя, может показаться запутанным, но он совершенно легитимен. Теги Struct и typedefs находятся в отдельных пространствах имен (в смысле C, а не в смысле С++); тег структуры может появляться сразу после ключевого слова struct.

Другая альтернатива - отделить typedef от определения структуры:

typedef struct Element Element;

struct Element {
    char value;
    Element *next;
};

(Вы можете использовать неполное имя типа в typedef.)

Ответ 2

Ваш первый вариант верен. Ваш второй вариант не делает того, что, по-видимому, делает.

В C он действителен для пересылки-объявления типа структуры в любом месте, даже в середине объявления чего-то еще. (Правила определения области охвата таких деклараций вступают в тупик до такой степени, что я не собираюсь их объяснять - достаточно сказать, что вам следует избегать этого). Вот почему вы не получаете ошибку на второй конструкции. Но что это значит для компилятора:

struct _Anonymous_1 // name not actually accessible to code
{
    char value;
    struct Element *next;
};
typedef struct _Anonymous_1 Element;

После этого кода тип "struct Element" полностью не связан с типом "Элемент" и не был полностью объявлен. Если вы попытаетесь использовать этот тип, например, в

char cadr(Element *cons)
{
    return cons->next->value;
}

компилятор не будет счастлив:

test.c: In function ‘cadr’:
test.c:9:22: error: dereferencing pointer to incomplete type

Альтернативой вашему первому варианту, который позволяет использовать "Элемент" вместо "Структурный элемент" везде, в том числе внутри определения типа, является

typedef struct Element Element;
struct Element
{
    char value;
    Element *next;
};

Но в C нет способа избежать необходимости вручную убедиться, что "struct Element" - это то же самое, что и "Element". Если вы не хотите иметь дело с этим, С++ ждет вас там & longrightarrow;

Ответ 3

Чтобы сохранить указатель на структуру, компилятору не нужно знать его содержимое или размер - просто размер указателя.

В первом примере struct Element является неполным типом до определения структуры, но это работает, потому что вы только объявляете указатель на него, а не экземпляр самой структуры.

В вашем втором примере вы вообще не объявляете структуру struct Element (struct Element и Element - это не одно и то же). Хотя вы все еще можете включить указатель в структуру, он НЕ относится к одному типу, он относится к struct Element, который не был определен. Определение структуры в вашем typedef является анонимной структурой, поэтому вы сможете ссылаться на нее только с Element (без ключевого слова struct). Таким образом, второй пример не будет работать должным образом.