自己参照構造体定義?


134

私は長い間Cを書いていないので、このような種類の再帰的な処理をどのように行えばよいのかわかりません...各セルに別のセルを含めたいのですが、エラーが発生します「フィールド「子」の型が不完全です」の行。調子はどう?

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

9
PS実際には、「構造体セル」から「セル」にtypedefします(これは一般的なパターンです)
David Z

彼はおそらくC ++コンパイラを使用しています。それは本当にC.だならば、彼はまた、_Boolを使用する必要があります
nabiy

それが本当にCである場合、彼はintを使用しているはずです:-)
paxdiablo 2009

2
どうして?C99にはブール値があります-<stdbool.h>をインクルードする必要があるだけです
Jonathan Leffler

回答:


184

明らかに、セルは他のセルを含むことはできません。それが終わることのない再帰になるからです。

ただし、セルには別のセルへのポインタを含めることができます。

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

7
@ cs01いいえ、Cellまだ対象範囲外です。
fredoverflow

1
それ理にかなっています。Pythonはそれを可能にし、そのようなオブジェクトの直列化さえも可能にします。なぜC ++ではないのですか?
noɥʇʎԀʎzɐɹƆ

1
に割り当てようとすると警告が表示さCell*cell->childます。
トマーシュザト-モニカを復活

3
@noɥʇʎԀʎzɐɹƆPythonはポインタを抽象化するので、気付かないからです。以来、structCでsは基本的に互いに隣接してそれらの値のすべてをストア(その構造体は無限のサイズのメモリ構造をもたらす、別のものを含有し、そうでなければならないので)、実際にそれ自体で構造体を格納することは不可能です。
jazzpi 2017

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

理論的な観点から、言語は自己参照構造のみをサポートでき、自己包括的構造はサポートできません。


実用的な観点から見ると、このような「構造体セル」のインスタンスは実際にはどのくらいの大きさでしょうか。
マーシュレイ

26
ほとんどのマシンでは、それより4バイト大きい。
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
タイラークロンプトン、2015年

2
@TylerCrompton上記のコードブロックが単一のCソースファイルに配置されいる場合、typedef 「コンパイラーによって実行」され、余分なstruct Cell;冗長性が生じます。ただし、何らかの理由で最後の2行をヘッダーファイルに入れ、最初の4行で構造体を定義する前にインクルードする場合、余分なものが必要です。Cellstruct Cell;
yyny

これはC99標準ではコンパイルできません。
トマーシュザト-モニカを復活させる'11

2
@YoYoYonnYいいえ、あなたはまだだけ書くことができtypedef struct Cell Cell;、それが作るCellの別名をstruct Cell。コンパイラがstruct Cell { .... }以前に見たことがあるかどうかは関係ありません。
メルポメン

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;

上記のコードフラグメントで言及した2つのケースのいずれかで、子Cell構造体をポインターとして宣言する必要があります。そうしないと、「フィールド '子'の型が不完全です」というエラーが発生します。その理由は、コンパイラーが使用されるときに割り振るスペースの量をコンパイラーが知るためには、「struct Cell」を定義する必要があるためです。

「struct 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;

しかし、最後のオプションは、通常は実行したくない余分な行と単語を増やします(私たちはあなたが知っているのでとても怠惰です;))。ビュー2をお勧めします。


typedef構文の説明が正しくありません(たとえばtypedef int (*foo)(void);)。ビュー1とビュー2の例は機能しません。これらstruct Cellは不完全な型になるため、実際にchildコードで使用することはできません。
メルポメン

3

もう1つの便利な方法は、次のように構造体タグで構造体を事前にtypedefすることです。

//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が使用するメモリの量を知るには、メモリの量を決定する必要があります) struct Aは!)をとります。

しかし、参照型は異なります。構造体「A」がそれ自体の型のインスタンスへの「参照」を含む場合、それに割り当てられているメモリの量はまだわかりませんが、メモリに割り当てられているメモリの量はわかりますアドレス(つまり、参照)。

HTH

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.