визначення референтних структур?


134

Я дуже довго не пишу С, і тому я не впевнений у тому, як мені слід займатися подібними рекурсивними речами ... Я хотів би, щоб кожна клітина містила ще одну клітинку, але я отримую помилку рядки "поле" дитина "має неповний тип". Як справи?

typedef struct Cell {
  int isParent;
  Cell child;
} Cell;

9
PS Насправді це typedefs "структура Cell" на "Cell" (це загальна модель)
David Z

він, ймовірно, використовує компілятор C ++. він також повинен використовувати _Bool, якщо це дійсно C.
nabiy

Він повинен використовувати int, якщо це справді C :-)
paxdiablo

2
Чому? C99 має bool - потрібно просто включити <stdbool.h>
Джонатан Леффлер,

Відповіді:


184

Очевидно, що Клітина не може містити іншу клітинку, оскільки вона стає нескінченною рекурсією.

Однак комірка CAN може містити вказівник на іншу клітинку.

typedef struct Cell {
  bool isParent;
  struct Cell* child;
} Cell;

7
@ cs01 Ні, Cellпоки не входить у сферу застосування.
fredoverflow

1
Це мало б сенс. Python дозволяє це і навіть дозволяє серіалізацію такого об’єкта. Чому б не C ++?
noɥʇʎԀʎzɐɹƆ

1
Я отримую попередження, коли намагаюся призначити Cell*їх cell->child.
Томаш Зато - Відновіть Моніку

3
@ noɥʇʎԀʎzɐɹƆ Тому що Python абстрагує вказівники, щоб ви їх не помічали. Оскільки structs в C в основному просто зберігають усі їх значення поруч, неможливо фактично зберігати структуру в собі (тому що ця структура повинна містити ще одну і так далі, що веде до структури пам'яті нескінченного розміру) .
джазпі

1
Пояснення щодо використання struct Cellдив. У цій відповіді .
wizzwizz4

26

У C ви не можете посилатися на typedef, який ви створюєте, використовуючи саму структуру. Ви повинні використовувати назву структури, як у наступній програмі тестування:

#include <stdio.h>
#include <stdlib.h>

typedef struct Cell {
  int cellSeq;
  struct Cell* next; /* 'tCell *next' will not work here */
} tCell;

int main(void) {
    int i;
    tCell *curr;
    tCell *first;
    tCell *last;

    /* Construct linked list, 100 down to 80. */

    first = malloc (sizeof (tCell));
    last = first;
    first->cellSeq = 100;
    first->next = NULL;
    for (i = 0; i < 20; i++) {
        curr = malloc (sizeof (tCell));
        curr->cellSeq = last->cellSeq - 1;
        curr->next = NULL;
        last->next = curr;
        last = curr;
    }

    /* Walk the list, printing sequence numbers. */

    curr = first;
    while (curr != NULL) {
        printf ("Sequence = %d\n", curr->cellSeq);
        curr = curr->next;
    }

    return 0;
}

Хоча це, мабуть, набагато складніше, ніж це у стандарті, ви можете вважати це компілятором, який знає про struct Cellперший рядок, typedefале не знає tCellдо останнього рядка :-) Ось як я пам’ятаю це правило.


про c ++, будь ласка, зв’яжіть відповіді щодо c ++
rimalonfire

@rimiro, питання було на C. Якщо ви хочете відповісти на варіант C ++, вам слід задати це як питання.
paxdiablo

16

З теоретичної точки зору, Мови можуть підтримувати лише самореференційні структури, а не самовключені структури.


З практичної точки зору, наскільки насправді був би такий екземпляр "struct Cell"?
Марш Рей

26
У більшості машин на чотири байти більше, ніж у себе.
TonyK

13

Існує щось подібне:

struct Cell {
  bool isParent;
  struct Cell* child;
};

struct Cell;
typedef struct Cell Cell;

Якщо ви оголосите це таким чином, він належним чином повідомляє компілятору, що структура Cell та plain-ol'-cell однакові. Таким чином, ви можете використовувати Cell як звичайне. Але все ж доведеться використовувати struct Cell всередині самої початкової декларації.


9
чому ти struct Cell;знову писав ?
МАКЗ

@MAKZ, оскільки typedef не був виконаний компілятором на той час, коли він компілював визначення struct Cell.
Тайлер Кромптон

2
@TylerCrompton якщо наведений вище код блок поміщається в окремий вихідний C файл, то ЬурейеЕ вже був «виконаний компілятором», що робить додатковий struct Cell;зайвим. Однак, якщо з якоїсь - то причини ви поклали останні два рядки в файл заголовка , який ви включаєте , перш ніж ви визначаєте - Cellструктуру з першими чотирма лініями, то додатковий struct Cell;є nececairy.
yyny

Це навіть не компілюється за стандартом C99.
Томаш Зато - Відновити Моніку

2
@YoYoYonnY Ні, ви все одно можете просто написати, typedef struct Cell Cell;і це зробить Cellпсевдонім для struct Cell. Не має значення, чи компілятор бачив struct Cell { .... }раніше.
мельпомена

8

Я знаю, що ця публікація стара, проте для отримання ефекту, який ви шукаєте, ви можете спробувати наступне:

#define TAKE_ADVANTAGE

/* Forward declaration of "struct Cell" as type Cell. */
typedef struct Cell Cell;

#ifdef TAKE_ADVANTAGE
/*
   Define Cell structure taking advantage of forward declaration.
*/
struct Cell
{
   int isParent;
   Cell *child;
};

#else

/*
   Or...you could define it as other posters have mentioned without taking
   advantage of the forward declaration.
*/
struct Cell
{
   int isParent;
   struct Cell *child;
};

#endif

/*
    Some code here...
*/

/* Use the Cell type. */
Cell newCell;

У будь-якому з двох випадків, згаданих у фрагменті коду вище, ОБОВ'ЯЗКОВО оголосити структуру клітин своєї дитини як вказівник. Якщо цього не зробити, ви отримаєте помилку "поле" дитина "має неповний тип". Причина полягає в тому, що "структура Cell" повинна бути визначена для того, щоб компілятор знав, скільки місця буде виділено при її використанні.

Якщо ви спробуєте використати "struct Cell" всередині визначення "stru Cell", тоді компілятор ще не може знати, скільки місця має "структура Cell". Однак компілятор вже знає, скільки місця займає вказівник, і (при прямому оголошенні) він знає, що "Cell" - це тип "Stru Cell" (хоча він ще не знає, наскільки великий "struct Cell" ). Отже, компілятор може визначити "Cell *" у структурі, яка визначається.


3

Перейдемо до базового визначення typedef. typedef використовують для визначення псевдоніму для існуючого типу даних або він визначений користувачем, або вбудований.

typedef <data_type> <alias>;

наприклад

typedef int scores;

scores team1 = 99;

Плутанина тут відбувається з самореференційною структурою, обумовленою членом одного типу даних, який не визначено раніше. Отже, стандартним способом ви можете записати свій код у вигляді: -

//View 1
typedef struct{ bool isParent; struct Cell* child;} Cell;

//View 2
typedef struct{
  bool isParent;
  struct Cell* child;
} Cell;

//Other Available ways, define stucture and create typedef
struct Cell {
  bool isParent;
  struct Cell* child;
};

typedef struct Cell Cell;

Але останній варіант збільшить кілька зайвих рядків і слів, як правило, ми цього не хочемо робити (ми так ліниві ви знаєте;)). Тому віддайте перевагу View 2.


Ваше пояснення typedefсинтаксису неправильне (врахуйте, наприклад, typedef int (*foo)(void);). Приклади перегляду 1 і перегляду 2 не працюють: вони створюють struct Cellнеповний тип, тому ви фактично не можете використовувати їх childу коді.
мельпомена

3

Ще одним зручним методом є попереднє введення структури за допомогою тегу структури як:

//declare new type 'Node', as same as struct tag
typedef struct Node Node;
//struct with structure tag 'Node'
struct Node
{
int data;
//pointer to structure with custom type as same as struct tag
Node *nextNode;
};
//another pointer of custom type 'Node', same as struct tag
Node *node;

1

Структура, яка містить посилання на себе. Поширене це в структурі, яка описує вузол для списку посилань. Кожен вузол потребує посилання на наступний вузол ланцюга.

struct node
{
       int data;
       struct node *next; // <-self reference
};

1

Усі попередні відповіді чудові, я просто думав дати зрозуміти, чому структура не може містити екземпляр власного типу (не посилання).

дуже важливо зауважити, що структури - це типи "value", тобто вони містять фактичне значення, тому коли ви оголошуєте структуру, компілятор повинен вирішити, скільки пам'яті виділити для її примірника, тож він проходить через усі його члени та додає збільшити їх пам'ять, щоб визначити всю пам'ять структури, але якщо компілятор знайшов екземпляр тієї самої структури всередині, то це парадокс (тобто для того, щоб знати, скільки займає структура пам'яті A, ви повинні вирішити, скільки пам'яті Структура А займає!).

Але типи посилань різні, якщо структура "A" містить "посилання" на екземпляр власного типу, хоча ми ще не знаємо, скільки пам'яті їй виділено, ми знаємо, скільки пам'яті виділено на пам'ять адреса (тобто посилання).

HTH

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.