【问题标题】:self referential struct definition?自引用结构定义?
【发布时间】:2010-10-09 23:19:45
【问题描述】:

我写 C 的时间不长,所以我不确定我应该如何去做这些递归的事情......我希望每个单元格包含另一个单元格,但我得到一个“字段‘孩子’的类型不完整”的错误。怎么了?

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

【问题讨论】:

  • PS 实际上它将“struct Cell”类型定义为“Cell”(这是一种常见模式)
  • 他可能正在使用 C++ 编译器。如果真的是 C,他也应该使用 _Bool。
  • 如果真的是 C,他应该使用 int :-)
  • 为什么? C99 有 bool - 你只需要包含

标签: c recursion struct typedef


【解决方案1】:

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

但是,Cell 可以包含指向另一个 Cell 的指针。

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

【讨论】:

  • @cs01 不,Cell 尚未在范围内。
  • 有意义。 Python 允许它,甚至允许对这样的对象进行序列化。为什么不是 C++?
  • 当我尝试将 Cell* 分配给 cell->child 时收到警告。
  • @noɥʇʎԀʎzɐɹƆ 因为 Python 抽象出指针,所以你不会注意到它们。由于 C 中的 structs 基本上只是将它们的所有值彼此相邻存储,因此实际上不可能在其自身中存储一个结构(因为该结构必须包含另一个结构,依此类推,导致内存结构为无限大)。
  • struct Cell的使用说明见this answer
【解决方案2】:

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

虽然它在标准中可能比这复杂得多,但您可以将其视为编译器在typedef 的第一行知道struct Cell,但直到最后一行才知道tCell: -) 这就是我记得那条规则的方式。

【讨论】:

  • c++ 怎么样,请您链接有关 c++ 的答案
  • @rimiro,问题是 C 问题。如果您想要 C++ 变体的答案,您应该将其作为问题提出
【解决方案3】:

从理论上讲,语言只能支持自引用结构,不能支持自包含结构。

【讨论】:

  • 从实际的角度来看,'struct Cell' 这样的实例实际上会有多大?
  • 在大多数机器上,比自身大四个字节。
【解决方案4】:

有办法解决这个问题:

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

struct Cell;
typedef struct Cell Cell;

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

【讨论】:

  • 为什么又写struct Cell;
  • @MAKZ 因为编译器在编译struct Cell 的定义时尚未执行 typedef。
  • @TylerCrompton 如果将上述代码块放入单个 C 源文件中,则 typedef 已“由编译器执行”,从而使额外的 struct Cell; 多余.但是,如果由于某种原因,您将最后两行放入包含 before 的头文件中,则使用前四行定义 Cell 结构,然后额外的struct Cell; 是必需的。
  • 这甚至不能在 C99 标准下编译。
  • @YoYoYonnY 不,您仍然可以只写typedef struct Cell Cell;,它会使Cell 成为struct Cell 的别名。编译器之前是否见过struct Cell { .... }并不重要。
【解决方案5】:

我知道这篇文章很旧,但是,为了获得您想要的效果,您可能需要尝试以下操作:

#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' 的类型不完整”错误。原因是必须定义“struct Cell”,以便编译器知道在使用时要分配多少空间。

如果您尝试在“struct Cell”的定义中使用“struct Cell”,那么编译器还不知道“struct Cell”应该占用多少空间。但是,编译器已经知道指针占用了多少空间,并且(通过前向声明)它知道“Cell”是“struct Cell”的一种类型(尽管它还不知道“struct Cell”有多大)。因此,编译器可以在正在定义的结构中定义一个“Cell *”。

【讨论】:

    【解决方案6】:

    另一种方便的方法是使用结构标签预先对结构进行类型定义:

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

    【讨论】:

      【解决方案7】:

      让我们看一下 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
      【解决方案8】:

      包含对自身的引用的结构。这种情况在描述链接列表节点的结构中很常见。每个节点都需要对链中下一个节点的引用。

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

      【讨论】:

        【解决方案9】:

        所有以前的答案都很棒,我只是想提供一个关于为什么结构不能包含其自己类型的实例(不是引用)的见解。

        非常重要的是要注意结构是“值”类型,即它们包含实际值,所以当你声明一个结构时,编译器必须决定分配多少内存给它的实例,所以它会遍历它的所有成员并添加他们的内存来计算结构的全部内存,但是如果编译器在内部找到相同结构的实例,那么这是一个悖论(即,为了知道结构 A 需要多少内存,您必须决定struct A 需要多少内存!)。

        但是引用类型是不同的,如果一个结构'A'包含一个对它自己类型的实例的'引用',虽然我们还不知道分配了多少内存,但我们知道分配了多少内存到内存地址(即引用)。

        HTH

        【讨论】:

          猜你喜欢
          • 2013-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-14
          • 2022-10-06
          相关资源
          最近更新 更多