自我参照结构定义?


134

我已经很长时间没有写C了,所以我不确定应该如何去做这些递归的事情……我希望每个单元格都包含另一个单元格,但是随着“字段'child'的行类型不完整”。这是怎么回事?

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

9
PS实际上,它将type定义为“ struct Cell”到“ Cell”(这是一种常见模式)
David Z

他可能正在使用C ++编译器。如果确实是C,他也应该使用
_Bool。– nabiy

如果真的是C,他应该使用int :-)
paxdiablo

2
为什么?C99具有布尔值-您只需要包括<stdbool.h>
乔纳森·莱夫勒

Answers:


184

显然,一个单元不能包含另一个单元,因为它变成了永无止境的递归。

但是,一个单元格可以包含指向另一个单元格的指针。

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

7
@ cs01不,Cell不在范围内。
fredoverflow

1
有道理的。Python允许,甚至允许序列化此类对象。为什么不使用C ++?
noɥʇʎԀʎzɐɹƆ

1
尝试分配Cell*给时,我会收到警告cell->child
托马什Zato -恢复莫妮卡

3
@noɥʇʎԀʎzɐɹƆ因为Python抽象了指针,所以您不会注意到它们。由于structC中的s基本上只是将它们的所有值彼此相邻地存储,因此实际上不可能在自身中存储一个结构(因为该结构将必须包含另一个结构,依此类推,从而导致无限大的内存结构) 。
jazzpi

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”实例实际上有多大?
Marsh Ray,2009年

26
在大多数计算机上,其自身大于四个字节。
TonyK 2010年

13

有一种解决方法:

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

struct Cell;
typedef struct Cell Cell;

如果这样声明,它将正确地告诉编译器struct Cell和plain-ol'-cell是相同的。因此,您可以像平常一样使用Cell。虽然还必须在初始声明本身内部使用struct Cell。


9
你为什么又写struct Cell;
MAKZ 2015年

@MAKZ,因为在编译的定义时,编译器尚未执行typedef struct Cell
泰勒·克朗普顿

2
@TylerCrompton如果上面的代码块被放入单个C源文件,则的typedef 被“由编译器执行的”,使得额外的struct Cell;冗余。但是,如果由于某种原因在将前两行定义结构之前,将后两行放入要包含的头文件中Cell多余的部分struct Cell;是必要的。
yyny

这甚至不能在C99标准下编译。
托马什Zato -恢复莫妮卡

2
@YoYoYonnY不,您仍然可以只写typedef struct Cell Cell;它,它将成为Cell的别名struct Cell。编译器是否曾经见过都没关系struct Cell { .... }
melpomene

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结构作为指针。如果您不这样做,那么您将收到“字段'child'has notcomplete type”错误。原因是必须定义“结构单元”,以便编译器知道使用时分配多少空间。

如果尝试在“ struct Cell”的定义内使用“ struct Cell”,则编译器尚无法知道“ struct Cell”应该占用多少空间。但是,编译器已经知道指针占用了多少空间,并且(带有前向声明)它已经知道“ Cell”是“ struct 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);)。您的View 1和View 2示例不起作用:它们struct Cell的类型不完整,因此您实际上不能child在代码中使用。
melpomene '18

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

以前的所有答案都很棒,我只是想深入了解为什么结构不能包含其自身类型的实例(而不是引用)。

重要的是要注意结构是“值”类型,即它们包含实际值,因此,在声明结构时,编译器必须决定为实例分配多少内存,以便它遍历所有成员并添加他们的内存以找出该结构的全部内存,但是如果编译器在内部找到同一结构的实例,则这是一个悖论(即,为了知道结构A需要多少内存,您必须决定多少内存结构A需要!)。

但是引用类型不同,如果结构'A'包含对自身类型实例的'引用',尽管我们尚不知道为其分配了多少内存,但我们知道为内存分配了多少内存地址(即参考)。

高温超导

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.