Почему "typedef struct {struct S * s;} S;"содержащий указатель на компиляцию того же типа? - PullRequest
21 голосов
/ 19 сентября 2011

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

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

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

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

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

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

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

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

Ответы [ 3 ]

22 голосов
/ 19 сентября 2011

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

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

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

Декларация

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

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

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

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

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

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

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

typedef struct Element Element;

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

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

4 голосов
/ 19 сентября 2011

Ваш первый вариант правильный. Ваш второй вариант не делает то, что кажется.

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

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

После этого кода тип "struct Element" совершенно не связан с типом "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". Если вы не хотите иметь с этим дело, C ++ ждет вас там & longrightarrow;

0 голосов
/ 19 сентября 2011

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

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

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

...