web-dev-qa-db-ja.com

循環typedef依存関係を解決しますか?

これらの構造体をtypedefする際に、次の循環依存関係を解決するための最良の方法は何ですか?
C言語タグに注意してください-標準のgcc Cで解決策を探しています。

typedef struct {
    char* name;
    int age;
    int lefthanded;
    People* friends;
} Person;

typedef struct {
    int count;
    int max;
    Person* data;
} People;
33
Nick Van Brunt

構造体の1つを前方宣言します。


struct people;

typedef struct {
  /* same as before */
  struct people* friends;
} Person;

typedef struct people {
  /* same as before */
} People;
28

答えは、宣言と定義の違いにあります。同じステップで宣言および定義しようとしています(typedefを介した新しいタイプの場合)。これらをさまざまなステップに分割して、コンパイラーが事前に何について話しているかを認識できるようにする必要があります。

typedef struct Person Person;
typedef struct People People;

struct Person {
    char* name;
    int age;
    int lefthanded;
    People* friends;
};

struct People {
    int count;
    int max;
    Person* data;
};

上部に2つの「空の」typedefが追加されていることに注意してください(宣言)。これは、新しい型Personが型 'struct Person'であることをコンパイラーに通知するため、struct Peopleの定義内でそれを見ると、それが何を意味するのかがわかります。

あなたの特定のケースでは、それが定義される前に使用される唯一のタイプであるため、Peopletypdefを事前に宣言するだけで実際に回避することができます。構造体Peopleの定義に入るまでに、タイプPersonはすでに完全に定義されています。したがって、以下も機能しますが、壊れやすいため非推奨です。

typedef struct People People;

typedef struct {
    char* name;
    int age;
    int lefthanded;
    People* friends;
} Person;

struct People {
    int count;
    int max;
    Person* data;
};

構造体定義の順序を入れ替えると(struct PeopleをPersonのtypedefの上に移動すると)、再び失敗します。これが脆弱な理由であるため、お勧めしません。

指定されたタイプの構造体へのポインタではなく構造体を含める場合、このトリックは機能しないことに注意してください。したがって、たとえば、次のコンパイルされません

typedef struct Bar Bar;

struct Foo
{
    Bar bar;
};

struct Bar
{
    int i;
};

上記のコードでは、struct Fooの定義内で使用しようとすると、タイプBarが不完全であるため、コンパイラエラーが発生します。つまり、その時点で構造体バーの定義が表示されていないため、構造体メンバー「バー」に割り当てるスペースの量がわかりません。

このコードコンパイルされます

typedef struct Foo Foo;
typedef struct Bar Bar;
typedef struct FooBar FooBar;

struct Foo
{
    Bar *bar;
};

struct Bar
{
    Foo *foo;
};

struct FooBar
{
    Foo     foo;
    Bar     bar;
    FooBar  *foobar;
};

これは、FooとBar内の円形ポインターでも機能します。これは、タイプ「Foo」と「Bar」が事前に宣言されているため(ただし、まだ定義されていないため)、コンパイラーがそれらへのポインターを作成できるためです。

FooBarの定義に取り掛かるまでに、FooとBarの両方の大きさを定義して、実際のオブジェクトをそこに含めることができるようにしました。型を事前に宣言しているので、型FooBarへの自己参照ポインターを含めることもできます。

StructFooBarの定義をstructFooまたはBarの定義の上に移動した場合、前の例と同じ理由(不完全な型)でコンパイルされないことに注意してください。

43
James

読みやすさについて:

typedef struct Foo_ Foo;
typedef struct Bar_ Bar;

struct Foo_ {
    Bar *bar;
};

struct Bar_ {
    Foo *foo;
};

typedef structを完全に回避することをお勧めします。

6
diapir

PersonPeopleへのポインタを必要としているだけなので、後者を事前に宣言するだけで問題ありません。

typedef struct People People;

次に、2番目の宣言を次のようにstructタグを使用して宣言するように変更します。

struct People {
    int count;
    int max;
    Person data[];
};
2
unwind
struct _People;

typedef struct {
    char* name;
    int age;
    int lefthanded;
    struct _People* friends;
} Person;

struct _People {
    int count;
    int max;
    Person data[1];
};

注:はPerson data[];標準?

1
Serge Wautier
struct People_struct;

typedef struct {
    char* name;
    int age;
    int lefthanded;
    struct People_struct* friends;
} Person;

typedef struct People_struct {
    int count;
    int max;
    Person data[];
} People;
0
pjc50