Как определить тип структуры, включая указатели на себя?

Код без typedef (и он работает):

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

Я пытаюсь создать код с использованием typedef для структуры «Узел» в двусвязном списке, но это не работает:

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

Есть ли способ обойти это с помощью typedef?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
1 999
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Внутри typedef определяемый тип еще не известен, поэтому вам необходимо ввести и использовать тег структуры:

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

Node_tag - это структурный тег из-за того, где он введен (а не из-за части имени «_tag»). Это не тип сам по себе, а только комбинация, поскольку struct Node_tag является типом, который может использоваться для членов структуры. Впоследствии, когда typedef завершен, тип Node был определен. Чтобы уточнить, возможно использование второго typedef как typedef struct Node_tag NodeToo;. Это демонстрирует, что тип struct Node_tag также можно использовать. Вот почему я предпочитаю использовать фрагмент имени "_tag", чтобы было понятно, что используется.

Вы можете использовать предварительное объявление структуры

typedef struct sNode Node; // this is a typedef and a declaration of the struct
struct sNode{
    int data;
    Node *next;
    Node *prev;
};

Таким образом, Node известен (но не определен) в определении вашего struct.

Его можно сжать, как это делает Юннош. Но тогда вам нужно использовать нотацию struct Name внутри вашего объявления.

Таким образом, можно уже использовать имя typedefed, также необходимо предварительное объявление, если у вас есть некоторые циклические зависимости в ваших структурах.

Также можно использовать имя структуры в качестве определения типа:

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

Лично я предпочитаю первый стиль, мне он кажется «более понятным», но во втором примере нет ничего плохого, если только компилятор не из достандартной эпохи (до 1989 года).

Как указал Йенс Густедт, первый стиль может быть несовместимым, если он включен в C++.

Так что, возможно, мне стоит изменить свое предпочтение на первое.

@JHBonarius Хорошая ссылка, я добавил ее в свою библиотеку ссылок SO.

Yunnosch 15.06.2018 08:46

Нет особых причин использовать struct sNode. struct Node, то есть тот же идентификатор тега, что и для typedef.

Jens Gustedt 15.06.2018 08:47

@JensGustedt Я знаю, что часто используется одно и то же имя со структурой и без нее. Но я думаю, что это «грязно», и есть некоторые правила, требующие разных имен. Но я не знаю, насколько они распространены.

Kami Kaze 15.06.2018 08:55

@JensGustedt Кажется, с этой практикой даже могут быть проблемы. Спасибо JHBonarius

Kami Kaze 15.06.2018 09:08

@KamiKaze Но эти проблемы были с предстандартные компиляторы C. Для нового кода это не должно быть проблемой. С typedef struct Foo {} Foo; никогда не сталкивался.

cmaster - reinstate monica 15.06.2018 09:34

Чтобы поместить это в контекст, предварительный стандарт относится к компиляторам, которые не удосужились стать соответствующими стандарту около 30 лет. И нет, это не грязный, так и должно быть. Наличие разных имен для одного и того же типа очень сбивает с толку, и определение типа одного и того же идентификатора - это то, что нужно сделать, если вы хотите, чтобы заголовок был совместим с C++.

Jens Gustedt 15.06.2018 10:14

@JensGustedt Я включил это в свой ответ, но это все же C, и это действительно так. Существуют и другие вещи, которые являются совершенно допустимыми для C, которые нарушают работу компилятора C++ (без преобразования malloc afaik), поэтому совместимость с C++ - это хорошо (а иногда и обязательно), но сама по себе не является показателем для кода C.

Kami Kaze 15.06.2018 10:33

@KamiKaze, поэтому я сказал "совместимость с заголовком".

Jens Gustedt 15.06.2018 16:27

Внутри структуры вы действительно должны использовать типы, которые уже известны компилятору. Способ соответствовать этому правилу в вашем случае - используйте тип struct Node * после объявления typedef struct Node. В этом случае, даже если тип struct Node не полностью определен, когда компилятор обнаруживает его внутри структуры, имя известно, и компилятор знает размер указателя (для резервирования в памяти), поэтому у него нет причин жаловаться!
Более того, также принято использовать то же имя для типа, что и после struct: вы можете использовать Node для обоих. Вот мое предлагаемое решение:

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

Несмотря на поддержку, я не считаю хорошей практикой давать структуре то же имя, что и тип. Не рекомендуется для совместимости (с древними компиляторами

JHBonarius 15.06.2018 09:04

@Laurent Хорошо, спасибо! К сожалению, я выбрал другой ответ. Тем не менее, ваше объяснение очень помогает, и я поддержал ваш ответ :-)

Richard 15.06.2018 09:26

@WealthyPlayer Добро пожаловать, спасибо за ваш голос. Мой ответ действительно был немного запоздалым, но я постарался, чтобы он был полным. Рад видеть, что это было полезно!

Laurent H. 15.06.2018 09:44

@WealthyPlayer Как правило, вы не привязаны к своему первому решению по выбранному ответу. Вы должны выбрать то, что считаете подходящим.

Kami Kaze 15.06.2018 10:37

@Kami Отметил :-D Извините за очень поздний ответ на комментарий.

Richard 24.06.2018 08:44

Другие вопросы по теме