selbstreferenzielle Strukturdefinition?

134

Ich habe C nicht sehr lange geschrieben und bin mir daher nicht sicher, wie ich diese Art von rekursiven Dingen ausführen soll ... Ich möchte, dass jede Zelle eine andere Zelle enthält, aber ich erhalte einen Fehler entlang der Zeilen von "Feld 'Kind' hat unvollständigen Typ". Was geht?

typedef struct Cell {
  int isParent;
  Cell child;
} Cell;
Ziggy
quelle
9
PS Eigentlich tippt es "struct Cell" in "Cell" (das ist ein gängiges Muster)
David Z
Er verwendet wahrscheinlich einen C ++ - Compiler. er sollte auch _Bool verwenden, wenn es wirklich C. ist
nabiy
Er sollte int verwenden, wenn es wirklich C ist :-)
paxdiablo
2
Warum? C99 hat bool - Sie müssen nur <stdbool.h> einschließen
Jonathan Leffler
1
mögliches Duplikat von C: Zeiger auf Struktur in der Strukturdefinition
Jonathan Leffler

Antworten:

184

Es ist klar, dass eine Zelle keine andere Zelle enthalten kann, da dies zu einer nie endenden Rekursion wird.

Eine Zelle kann jedoch einen Zeiger auf eine andere Zelle enthalten.

typedef struct Cell {
  bool isParent;
  struct Cell* child;
} Cell;
Andrew Grant
quelle
7
@ cs01 Nein, Cellist noch nicht im Geltungsbereich.
Fredoverflow
1
Es würde Sinn machen. Python erlaubt es und erlaubt sogar die Serialisierung eines solchen Objekts. Warum nicht C ++?
noɥʇʎԀʎzɐɹƆ
1
Ich erhalte Warnungen , wenn ich versuche zuweisen Cell*zu cell->child.
Tomáš Zato - Wiedereinsetzung Monica
3
@ noɥʇʎԀʎzɐɹƆ Weil Python Zeiger abstrahiert, damit Sie sie nicht bemerken. Da structs in C im Grunde nur alle ihre Werte nebeneinander speichern, ist es unmöglich, eine Struktur tatsächlich in sich selbst zu speichern (da diese Struktur eine andere enthalten müsste usw., was zu einer Speicherstruktur von unendlicher Größe führt). .
Jazzpi
1
Eine Erklärung zur Verwendung von struct Cellfinden Sie in dieser Antwort .
wizzwizz4
26

In C können Sie nicht auf das Typedef verweisen, das Sie mit der Struktur selbst erstellen. Sie müssen den Strukturnamen wie im folgenden Testprogramm verwenden:

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

Obwohl es im Standard wahrscheinlich viel komplizierter ist als dies, können Sie sich vorstellen, dass der Compiler struct Cellin der ersten Zeile der, typedefaber tCellerst in der letzten Zeile davon weiß :-) So erinnere ich mich an diese Regel.

paxdiablo
quelle
Was ist mit C ++
? Können
@ Rimiro, die Frage war eine C-Frage. Wenn Sie die Antwort für eine C ++ Variante möchten, sollten Sie fragen , es als eine Frage.
Paxdiablo
16

Aus theoretischer Sicht können Sprachen nur selbstreferenzielle Strukturen unterstützen, keine selbstinklusiven Strukturen.

Sundar
quelle
Wie groß wäre eine solche Instanz von 'struct Cell' aus praktischer Sicht tatsächlich?
Marsh Ray
26
Auf den meisten Computern vier Bytes größer als er selbst.
TonyK
13

Es gibt eine Art Weg, dies zu umgehen:

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

struct Cell;
typedef struct Cell Cell;

Wenn Sie es so deklarieren, teilt es dem Compiler ordnungsgemäß mit, dass struct Cell und plain-ol'-cell identisch sind. Sie können Cell also wie gewohnt verwenden. Die Strukturzelle muss jedoch immer noch in der ursprünglichen Deklaration selbst verwendet werden.

Benjamin Horstman
quelle
9
warum hast du struct Cell;wieder geschrieben
MAKZ
@MAKZ, da das typedef zum Zeitpunkt der Kompilierung der Definition von noch nicht vom Compiler ausgeführt wurde struct Cell.
Tyler Crompton
2
@TylerCrompton Wenn der obige Codeblock in eine einzelne C-Quelldatei eingefügt wird, wurde das typedef "vom Compiler ausgeführt", wodurch das Extra struct Cell;redundant wird. Wenn Sie jedoch aus irgendeinem Grund die letzten beiden Zeilen in eine Header-Datei einfügen, die Sie einschließen, bevor Sie die CellStruktur mit den ersten vier Zeilen definieren, ist das Extra struct Cell;erforderlich.
yyny
Dies wird nicht einmal unter dem C99-Standard kompiliert.
Tomáš Zato - Wiedereinsetzung Monica
2
@YoYoYonnY Nein, du kannst immer noch einfach schreiben typedef struct Cell Cell;und es wird Cellein Alias ​​für erstellt struct Cell. Es spielt keine Rolle, ob der Compiler zuvor gesehen struct Cell { .... }hat.
Melpomene
8

Ich weiß, dass dieser Beitrag alt ist. Um jedoch den gewünschten Effekt zu erzielen, sollten Sie Folgendes versuchen:

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

In jedem der beiden im obigen Codefragment genannten Fälle MÜSSEN Sie Ihre untergeordnete Zellstruktur als Zeiger deklarieren. Wenn Sie dies nicht tun, wird der Fehler "Feld 'Kind' hat unvollständigen Typ" angezeigt. Der Grund dafür ist, dass "struct Cell" definiert werden muss, damit der Compiler weiß, wie viel Speicherplatz bei seiner Verwendung zugewiesen werden muss.

Wenn Sie versuchen, "struct Cell" in der Definition von "struct Cell" zu verwenden, kann der Compiler noch nicht wissen, wie viel Speicherplatz "struct Cell" einnehmen soll. Der Compiler weiß jedoch bereits, wie viel Speicherplatz ein Zeiger benötigt, und (mit der Vorwärtsdeklaration) weiß er, dass "Zelle" eine Art "Strukturzelle" ist (obwohl er noch nicht weiß, wie groß eine "Strukturzelle" ist ). Der Compiler kann also eine "Zelle *" innerhalb der Struktur definieren, die definiert wird.

Shawn
quelle
3

Lassen Sie uns die grundlegende Definition von typedef durchgehen. typedef wird verwendet, um einen Alias ​​für einen vorhandenen Datentyp zu definieren, der entweder benutzerdefiniert oder integriert ist.

typedef <data_type> <alias>;

beispielsweise

typedef int scores;

scores team1 = 99;

Verwechslung besteht hier mit der selbstreferenziellen Struktur aufgrund eines Mitglieds desselben Datentyps, der zuvor nicht definiert wurde. Standardmäßig können Sie Ihren Code also wie folgt schreiben: -

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

Aber letzte Option erhöht einige zusätzliche Zeilen und Wörter mit normalerweise wollen wir nicht (wir sind so faul, wie Sie wissen;)). Also lieber View 2.

Vineetv2821993
quelle
Ihre Erklärung der typedefSyntax ist falsch (zB berücksichtigen typedef int (*foo)(void);). Ihre Beispiele für Ansicht 1 und Ansicht 2 funktionieren nicht: Sie bilden struct Celleinen unvollständigen Typ, sodass Sie sie nicht childin Ihrem Code verwenden können.
Melpomene
3

Eine andere bequeme Methode besteht darin, die Struktur mit dem Struktur-Tag wie folgt vorab zu definieren:

//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;
Keynes
quelle
1

Eine Struktur, die einen Verweis auf sich selbst enthält. Dies tritt häufig in einer Struktur auf, die einen Knoten für eine Verknüpfungsliste beschreibt. Jeder Knoten benötigt einen Verweis auf den nächsten Knoten in der Kette.

struct node
{
       int data;
       struct node *next; // <-self reference
};
DARSHINI DESAI
quelle
1

Alle vorherigen Antworten sind großartig. Ich wollte nur einen Einblick geben, warum eine Struktur keine Instanz ihres eigenen Typs enthalten kann (keine Referenz).

Es ist sehr wichtig zu beachten, dass Strukturen 'Wert'-Typen sind, dh sie enthalten den tatsächlichen Wert. Wenn Sie also eine Struktur deklarieren, muss der Compiler entscheiden, wie viel Speicher einer Instanz davon zugewiesen werden soll, damit er alle seine Mitglieder durchläuft und hinzufügt Erhöhen Sie den Speicher, um den Gesamtspeicher der Struktur zu ermitteln. Wenn der Compiler jedoch eine Instanz derselben Struktur im Inneren gefunden hat, ist dies ein Paradoxon (dh um zu wissen, wie viel Speicher Struktur A benötigt, müssen Sie entscheiden, wie viel Speicher erforderlich ist Struktur A dauert!).

Referenztypen sind jedoch unterschiedlich, wenn eine Struktur 'A' eine 'Referenz' auf eine Instanz ihres eigenen Typs enthält, obwohl wir noch nicht wissen, wie viel Speicher ihr zugewiesen ist, wissen wir, wie viel Speicher einem Speicher zugewiesen ist Adresse (dh die Referenz).

HTH

m.eldehairy
quelle