2009-02-26 2 views
97

Я не писал C очень долго, и поэтому я не уверен, как мне следует делать такие рекурсивные вещи ... Я хотел бы, чтобы каждая ячейка содержала другую ячейку, но я получаю сообщение об ошибке по строкам «поле« ребенок »имеет неполный тип». Что происходит?определение самореференциальной структуры?

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

PS (Ziggy также явно смущена ЬурейиЙ: он typedefed Cell к Cell и удивляется, почему?)

+9

PS На самом деле это "Определения типов STRUCT Cell" на "Cell" (это общий шаблон) –

+1

И это происходит автоматически в C++. –

+0

Пожалуйста, вернитесь к C++, если это было вашим намерением (я предполагаю, что это было, так как вы приняли ответ, который не будет работать на C, и вы использовали bool (хотя это также можно было бы ввести typedef в C)). – paxdiablo

ответ

147

Очевидно, что клетка не может содержать другую клетку, как это становится нескончаемыми рекурсиями.

Однако ячейка CAN может содержать указатель на другую ячейку.

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

@ cs01 Нет, 'Cell' пока еще не доступен. – fredoverflow

+1

It * would * иметь смысл. Python позволяет и даже позволяет сериализовать такой объект. Почему не C++? –

+0

Я получаю предупреждения, когда пытаюсь назначить 'Cell *' 'cell-> child'. –

19

В C (в отличие от C++, где это возможно, я не проверял), вы не можете ссылаться на ЬурейеМ, что вы создаете жгуты самой структуры. Вы должны использовать имя структуры, как показано в следующей тестовой программы:

#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 до последней строки :-) Вот как я помню это правило.

11

Существует своего рода обходной путь:

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

struct Cell; 
typedef struct Cell Cell; 

Если вы объявляете это так, он правильно говорит компилятору, что структура клеток и равнинно-ol'-клетки являются одинаковыми. Таким образом, вы можете использовать Cell как обычно. Тем не менее все равно придется использовать struct Cell внутри самого исходного объявления.

+8

Почему вы писали 'struct Cell?' Снова? – MAKZ

+0

@MAKZ, потому что typedef не был выполнен компилятором в то время, когда он компилирует определение 'struct Cell'. –

+1

@TylerCrompton, если вышеуказанный блок кода помещен в один исходный файл C, тогда typedef _has_ был «выполнен компилятором», что делает избыточную «struct Cell» избыточной.Если, однако, по какой-то причине вы помещаете последние две строки в заголовочный файл, в который вы указываете _before_, вы определяете структуру «Ячейка» с четырьмя четырьмя строками, а затем * дополнительную «конструкцию Ячейка», - это nececairy. – YoYoYonnY

11

С теоретической точки зрения Языки могут поддерживать только самореферентные структуры, а не самосознающие структуры.

+0

С практической точки зрения, насколько велик бы такой экземпляр «структурной ячейки»? –

+18

На большинстве машин четыре байта больше, чем он сам. – TonyK

2

A Структура, содержащая ссылку на себя. Общее появление этого в структуре, которая описывает узел для списка ссылок. Каждому узлу нужна ссылка на следующий узел в цепочке.

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

Я знаю, что этот пост старый, однако, чтобы получить эффект, который вы ищете, вы можете попробовать следующее:

#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; 

В любом из двух случаев, указанных в коде фрагмент выше, вы ДОЛЖНЫ объявить свою дочернюю структуру ячеек как указатель. Если вы этого не сделаете, вы получите сообщение об ошибке «Поле» с неполным типом. Причина в том, что «struct Cell» должен быть определен для того, чтобы компилятор знал, сколько места выделяется при его использовании.

Если вы попытаетесь использовать «struct Cell» внутри определения «struct Cell», то компилятор еще не может определить, сколько пространства займет «ячейка». Однако компилятор уже знает, сколько пробелов занимает указатель, и (с помощью прямого объявления) он знает, что «Cell» - это тип «struct 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; 

Но последний вариант увеличить некоторые дополнительные строки и слова с обычно мы не хотим делать (мы настолько ленивы, вы знаете;)). Так предпочитают Просмотреть 2.

+0

Ваше объяснение синтаксиса 'typedef' неверно (рассмотрите, например,' typedef int (* foo) (void); '). Примеры View 1 и View 2 не работают: они делают 'struct Cell' неполным, поэтому вы не можете использовать' child' в своем коде. – melpomene

2

Еще один удобный способ для предварительной ЬурейеЕ структуры с, тег структуры, как:

//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; 
0

Все предыдущие ответы являются большими, я просто думал, чтобы дать представление о том, почему структура может 't содержит экземпляр своего типа (а не ссылку).

его очень важно отметить, что структуры являются «значениями» типов, то есть они содержат фактическое значение, поэтому, когда вы объявляете структуру, компилятор должен решить, сколько памяти будет выделено экземпляру, поэтому он проходит через все его членов и добавляет свою память, чтобы вычислить всю память структуры, но если компилятор нашел экземпляр одной и той же структуры внутри, то это парадокс (т. е. чтобы узнать, сколько памяти struct A вам нужно решите, сколько памяти занимает структура A!).

Но ссылочные типы различны, если структура 'A' содержит ссылку на экземпляр своего типа, хотя мы еще не знаем, сколько памяти выделено для нее, мы знаем, сколько памяти выделяется на адрес памяти (т. е. ссылку).

НТН

Смежные вопросы