2009-02-26 21 views
97

Nie pisałem C przez bardzo długi czas, więc nie jestem pewien, w jaki sposób powinienem wykonywać te rodzaje rekursywnych rzeczy ... Chciałbym, aby każda komórka zawierała inną komórkę, ale dostaję błąd wzdłuż linii "pola" dziecka "ma niepełny typ". Co tam?samodzielna definicja struktury odwołań?

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

PS (Ziggy jest także wyraźnie zdezorientowany typedef: on typedefed Cell do Cell i zastanawia się, dlaczego?)

+9

PS Właściwie to typedef struct "komórka" do "komórka" (to wspólny wzór) –

+1

I to jest automatyczna w C++. –

+0

Proszę retagować do C++, jeśli to był twój zamiar (zakładam, że było tak, odkąd przyjąłeś odpowiedź, która nie zadziała w C i użyłeś bool (chociaż to może być również wpisane w C)). – paxdiablo

Odpowiedz

147

Wyraźnie komórka nie może zawierać inną komórkę jako staje się niekończącym się rekurencji.

Jednak komórka CAN może zawierać wskaźnik do innej komórki.

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

@ cs01 Nie, 'Cell' jeszcze nie jest w zasięgu. – fredoverflow

+1

To * miałoby * sens. Python pozwala na to, a nawet umożliwia serializację takiego obiektu. Dlaczego nie C++? –

+0

Otrzymuję ostrzeżenia, gdy próbuję przypisać 'Cell *' do 'cell-> child'. –

19

W C (w przeciwieństwie do C++, gdzie może to być możliwe, nie zaznaczone), nie można odwołać typedef że jesteś tworzenie withing samą strukturę. Trzeba użyć nazwy struktury, jak w poniższym programie testowym:

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

Chociaż to chyba o wiele bardziej skomplikowany niż ten w normie, można myśleć o nim jako kompilatora Wiedząc o struct Cell na pierwszej linii z typedef, ale nie wiedząc o tCell aż do ostatniej linii :-) Tak właśnie pamiętam tę zasadę.

11

Jest swego rodzaju odwrót to:

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

struct Cell; 
typedef struct Cell Cell; 

Jeśli zadeklarujesz to tak, że właściwie informuje kompilator, że struktura komórkowa i zwykły ol' komórki są takie same. Więc możesz używać Cell tak jak zwykle. Nadal jednak trzeba użyć struct Cell wewnątrz samej deklaracji wstępnej.

+8

dlaczego napisałeś 'struct Cell;' again? – MAKZ

+0

@MAKZ, ponieważ typedef nie został wykonany przez kompilator w czasie kompilowania definicji 'struct Cell'. –

+1

@TylerCrompton, jeśli powyższy blok kodu zostanie wstawiony do pojedynczego pliku źródłowego C, wówczas typedef został "wykonany przez kompilator", czyniąc dodatkową zbroję 'struct Cell;' nadmiarową.Jeśli jednak z jakiegoś powodu umieściłeś dwie ostatnie linie w pliku nagłówkowym, do którego należy _ przedtem_, definiujesz strukturę 'Komórka' z pierwszymi czterema liniami, * wtedy * dodatkowa' struct komórka; 'jest nekrocedurą. – YoYoYonnY

11

Z teoretycznego punktu widzenia języki mogą wspierać tylko struktury samoodniosące się, a nie struktury samozatrudnione.

+0

Z praktycznego punktu widzenia, jak duży byłby taki przykład "komórki strukturalnej"? –

+18

Na większości maszyn, cztery bajty większe od siebie. – TonyK

2

Struktura, która zawiera odniesienie do samego siebie. Częstym zjawiskiem jest to w strukturze, która opisuje węzeł dla listy linków. Każdy węzeł potrzebuje odniesienia do następnego węzła w łańcuchu.

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

Znam ten post jest stary, jednak aby uzyskać efekt, którego szukasz, możesz spróbować wykonać następujące czynności:

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

W każdym z tych dwóch przypadków wymienionych w kodzie fragment powyżej, MUSISZ zadeklarować swoje dziecko Struktura komórki jako wskaźnik. Jeśli tego nie zrobisz, otrzymasz błąd "pole" dziecko "ma niekompletny typ". Powodem jest to, że "struct Cell" musi zostać zdefiniowany, aby kompilator wiedział, ile miejsca należy przydzielić, kiedy zostanie użyty.

Jeśli spróbujesz użyć "struct Cell" w definicji "struct Cell", to kompilator nie może jeszcze wiedzieć, ile miejsca ma "struct Cell". Jednak kompilator już wie, ile miejsca zajmuje wskaźnik i (z deklaracją forward) wie, że "Cell" jest typem "struct Cell" (chociaż nie wie jeszcze, jak duża jest "struct cell"). Tak więc kompilator może zdefiniować "komórkę *" wewnątrz definiowanej struktury.

3

Przejdźmy przez podstawową definicję typedef. typedef służy do definiowania aliasu do istniejącego typu danych, albo jest zdefiniowany przez użytkownika, albo wbudowany.

typedef <data_type> <alias>; 

np

typedef int scores; 

scores team1 = 99; 

Zamieszanie tutaj jest z samonośną referencyjnego, ze względu na członek danych tego samego typu, który nie jest określić wcześniej. Tak więc w standardowy sposób można napisać kod jak: -

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

Ale ostatnia opcja zwiększenia jakieś dodatkowe linie i rozmowę z reguły nie chcemy robić (jesteśmy tak leniwy wiesz;)). Więc wolą Zobacz 2.

+0

Twoje wyjaśnienie składni "typedef" jest niepoprawne (rozważ np. 'Typedef int (* foo) (void);'). Twoje przykłady Widok 1 i Widok 2 nie działają: powodują, że 'struct Cell' jest niekompletnym typem, więc nie możesz w rzeczywistości używać' child' w swoim kodzie. – melpomene

2

Innym wygodnym sposobem jest wstępnie typedef struktury, struktury z tag jak:

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

Wszystkie poprzednie odpowiedzi są świetne, pomyślałem, aby dać wgląd w dlaczego struktura może nie zawierają instancji własnego typu (nie odniesienia).

to bardzo ważne, aby zauważyć, że struktury są typami "wartościowymi", tzn. Zawierają rzeczywistą wartość, więc kiedy deklarujesz strukturę, kompilator musi zdecydować, ile pamięci przypisać do instancji, tak że przechodzi przez wszystkie jego członkowie i sumują swoją pamięć, aby obliczyć całą pamięć struktury, ale jeśli kompilator znalazł instancję o tej samej strukturze wewnątrz, to jest to paradoks (tj. aby wiedzieć, ile pamięci A zajmuje ci A zdecyduj, ile zajmuje pamięć klasy A!).

Ale typy odniesienia są różne, jeśli struktura "A" zawiera "odwołanie" do instancji własnego typu, chociaż nie wiemy jeszcze, ile pamięci jest do niego przydzielona, ​​wiemy, ile pamięci jest przydzielone do adresu pamięci (tj. odniesienia).

HTH