Cで構造体を頻繁にtypedefする必要があるのはなぜですか?


406

私は以下のような構造からなる多くのプログラムを見てきました

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

なぜそんなに頻繁に必要なのですか?特定の理由または該当する領域はありますか?


14
より完全で正確な答え:stackoverflow.com/questions/612328/…–
AbiusX

これには欠点struct * ptrがあります。構造体の内部の行がエラーの原因となるため、匿名の構造体でリンクリストを作成できないと思います
Raghib Ahsan

9
「より徹底し、正確な答えは」ある構造体やC ++でのtypedef構造体の違い、およびその答えは完全にC.についての質問には適していません作るこのエリアのCとC ++との間に有意な違いがある
ジョナサン・レフラー

この質問には、typedef構造体と構造体定義の重複があり、それらにも優れた答えがあります。
ジョナサンレフラー、2016年

2
OTOH、kernel.org / doc / html / v4.10 / process / coding-style.htmlは、そのようなtypedefを行うべきではないことを示しています。
glglgl 2018年

回答:


454

グレッグ・ヒューギルが言ったように、typedefは、あちこちに書く必要がないことを意味しますstruct。これはキーストロークを節約するだけでなく、smidgenをより抽象化するため、コードをよりクリーンにすることもできます。

のようなもの

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

「struct」キーワードをあちこちに表示する必要がない場合は、よりきれいになり、言語に「Point」と呼ばれるタイプが実際にあるかのように見えます。これは、の後にtypedef、私が推測するケースです。

また、あなたの例(と私)はstruct それ自体の名前を省略しましたが、実際に名前を付けることは、不透明なタイプを提供したい場合にも役立ちます。次に、たとえば次のようなコードをヘッダーに含めます。

typedef struct Point Point;

Point * point_new(int x, int y);

次にstruct、実装ファイルで定義を提供します。

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

後者の場合、その定義はヘッダーファイルのユーザーから隠されるため、値でポイントを返すことはできません。これは、たとえばGTK +で広く使用されている手法です。

更新このtypedef非表示の使用がstruct悪い考えと見なされている非常に評判の高いCプロジェクトもあることに注意してください。Linuxカーネルはおそらく最もよく知られているそのようなプロジェクトです。Linusの怒った言葉については、Linux Kernel CodingStyleドキュメントの第5章を参照してください。:)私のポイントは、結局のところ、問題の「べき」はおそらく固いものではないということです。


58
アンダースコアの後に大文字が続く識別子は使用しないでください。予約されています(セクション7.1.3パラグラフ1を参照)。多くの問題になる可能性は低いですが、それらを使用する場合、技術的に未定義の動作です(7.1.3パラグラフ2)。
dreamlax 2011年

16
@dreamlax:他の人にはっきりしない場合、それは識別子にアンダースコアと大文字で始めただけで、すべきではありません。識別子の途中で自由に使用できます。
brianmearns 2012年

12
ここで与えられた例(typedefが "struct"を "すべての場所で"使用できないようにする)は、typedefのない同じコードよりも実際に長いのは興味深いことです。得られた抽象化のsmidgenが、追加の難読化と比較して有利になることはほとんどありません。
ウィリアムパーセル2012年

7
@Rerito FYI、のページ166 C99のドラフトはアンダースコアと大文字のいずれかまたは別のアンダースコアで始まるすべての識別さERSは、常に使用するために予約されています。また、アンダースコアで始まるすべての識別子は、通常の名前空間とタグ名空間の両方でファイルスコープを持つ識別子として使用するために常に予約されています。
e2-e4 2013

10
興味深いことに、Linuxカーネルコーディングガイドラインでは、typedef(セクション5)の使用についてはより保守的である必要があるとしています:kernel.org/doc/Documentation/CodingStyle
gsingh2011

206

多くの人がこれを間違えているのは驚くべきことです。Cではtypedef構造体を使用しないでください。大規模なCプログラムでは通常非常に汚染されているグローバル名前空間を不必要に汚染します。

また、タグ名のないtypedefされた構造体は、ヘッダーファイル間の順序関係の不必要な強制の主な原因です。

検討してください:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

typedefを使用せずにそのような定義を使用すると、compilandユニットがfoo.hをインクルードしてFOO_DEF定義を取得することが可能です。foo構造体の「bar」メンバーを逆参照しようとしない場合は、「bar.h」ファイルを含める必要はありません。

また、名前空間はタグ名とメンバー名で異なるため、次のような非常に読みやすいコードを書くことができます。

struct foo *foo;

printf("foo->bar = %p", foo->bar);

名前空間は分離されているため、構造体タグ名と一致する変数の命名に競合はありません。

コードを維持する必要がある場合は、typedefされた構造体を削除します。


37
さらに驚くべきことは、この回答が与えられてから13か月後、私が最初に賛成票を投じるということです。構造体をtypedefすることは、Cの最大の悪用の1つであり、適切に記述されたコードには場所がありません。typedefは、複雑な関数ポインター型の難読化を解除するのに役立ち、実際には他の有用な目的は果たしません。
ウィリアムパーセル2012年

28
ピーター・ファン・デル・リンデンはまた、彼の啓蒙的な本「エキスパートCプログラミング-ディープCシークレット」で構造体の型定義に反対する訴訟を起こしています。要点は次のとおりです。何かが構造体または共用体であることを知りたいので、非表示にしないでください。
イェンス

34
Linuxカーネルのコーディングスタイルでは、typedefing構造体を明示的に禁止しています。第5章:Typedefs:「構造体とポインタにtypedefを使用するのは誤りです。」kernel.org/doc/Documentation/CodingStyle
jasso

63
正確に言うと、「struct」を何度も入力すると、どのようなメリットがありますか?そして汚染について言えば、グローバル名前空間に同じ名前の構造体と関数/変数/ typedefが必要なのはなぜですか(同じ関数のtypedefでない限り)?安全なパターンは使用することtypedef struct X { ... } Xです。そうすれXば、定義が使用可能な場所であればどこでも構造体をアドレス指定するために短い形式を使用できますがstruct X、必要に応じて前方宣言して使用できます。
Pavel Minaev 2013年

7
私は個人的にtypedefを使用することが非常にまれであり、他の人がそれを使用するべきではないと言っていません。それは私のスタイルではありません。変数型の前に構造体を表示するのが好きなので、その構造体をすぐにわかります。引数を入力するのは簡単ではありません。1文字の変数を入力するのも簡単です。また、今日ではどこでもstructを入力するのが難しいので、オートコンプリートを使用しています
ネイサンデイ

138

Dan Saksによる古い記事(http://www.ddj.com/cpp/184403396?pgno=3)から:


構造体の命名に関するC言語のルールは少し風変わりですが、無害です。ただし、C ++のクラスに拡張すると、これらの同じルールは、バグがクロールするための小さな亀裂を開きます。

Cでは、名前sは

struct s
    {
    ...
    };

タグです。タグ名はタイプ名ではありません。上記の定義を前提として、次のような宣言

s x;    /* error in C */
s *p;   /* error in C */

Cのエラーです。次のように記述する必要があります。

struct s x;     /* OK */
struct s *p;    /* OK */

共用体と列挙の名前も型ではなくタグです。

Cでは、タグは他のすべての名前(関数、型、変数、および列挙定数)とは異なります。Cコンパイラは、概念的には他のすべての名前を保持するテーブルから物理的に分離していないとしても、概念的にはシンボルテーブルにタグを保持します。したがって、Cプログラムは、同じスコープで同じスペルのタグと別の名前の両方を持つことができます。例えば、

struct s s;

タイプstruct sの変数sを宣言する有効な宣言です。良い方法ではないかもしれませんが、Cコンパイラはそれを受け入れる必要があります。Cがこのように設計された理由を説明したことはありません。いつも間違いだと思っていましたが、間違いです。

多くのプログラマー(本当にあなたを含む)は、構造体名をタイプ名と考えることを好むため、typedefを使用してタグのエイリアスを定義します。たとえば、定義

struct s
    {
    ...
    };
typedef struct s S;

次のように、構造体sの代わりにSを使用できます。

S x;
S *p;

プログラムは、型と変数(または関数または列挙定数)の両方の名前としてSを使用できません。

S S;    // error

これはいい。

構造体、共用体、または列挙型定義のタグ名はオプションです。多くのプログラマーは、次のように、構造体定義をtypedefに折りたたみ、タグを完全に省略します。

typedef struct
    {
    ...
    } S;

リンクされた記事には、Cが動作しないことを要求しないというC ++の動作が、typedef名前の非表示の微妙な問題をどのように引き起こすかについての議論もあります。これらの問題を防ぐために、typedef一見すると不要であるように見えても、C ++のクラスと構造体にも良い考えです。C ++では、typedef名前を非表示にすると、潜在的な問題の隠された原因ではなく、コンパイラーが通知するエラーになります。


2
タグ名が非タグ名と同じである1つの例は、int stat(const char *restrict path, struct stat *restrict buf)関数を使用する(POSIXまたはUnix)プログラムにあります。そこstatでは、通常の名前空間とstruct statタグ名前空間に関数があります。
ジョナサンレフラー

2
あなたの声明、SS; //エラー.... IS WRONGうまく動作します。つまり、「typedefタグとvarに同じ名前を付けることはできません」という記述は
間違ってい

63

typedef回避策を使用するとstruct、その型の変数を宣言するたびに書き込む必要がなくなります。

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

2
わかりました、C ++ではその問題は発生していません。では、Cコンパイラからグリッチを削除してC ++と同じにしないでください。C++にはいくつかの異なるアプリケーション領域があり、高度な機能を備えていますが、Cでそれらの一部を継承しないとオリジナルC?
Manoj Doubts

4
Manoj、それ自体を参照する構造体を定義する必要がある場合は、タグ名( "構造体foo")が必要です。たとえば、リンクされたリストの「次の」ポインタ。要点は、コンパイラーが標準を実装することであり、それは標準が行うように言っていることです。
マイケルカーマン

41
これはCコンパイラの不具合ではなく、設計の一部です。彼らはそれをC ++向けに変更しました。
Herms

5
残念ながら、多くの 'プログラマ'はstructを定義してから、何らかの関連のない名前(struct myStruct ...; typedef struct myStruct susan *など)でtypedefを定義しています。ほとんどすべての場合、typedefはコードを乱雑にし、実際の変数/パラメータであり、コードのオリジナルのライターを含むすべての人を
誤解させる

1
@ user3629249言及したプログラミングスタイルはひどいものだと私は同意しますが、これはtypedef一般的にING構造を否定する理由にはなりません。あなたも行うことができますtypedef struct foo foo;。もちろん、structキーワードはそれ以上必要ありません。これは、私たちが見る型エイリアスが構造体のエイリアスであることを示唆する有用なヒントになりますが、一般的に悪くはありません。結果の型エイリアスの識別子が、typedefそれが構造体fe:のエイリアスであることを示す場合も考えてくださいtypedef struct foo foo_struct;
RobertSは

38

常にtypedef enumsとstructsを使用するもう1つの理由は、この問題の結果です。

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

構造体のEnumDefのタイプミスに注意してください(Enu u Enu mDef)のください。これはエラー(または警告)なしでコンパイルされ、(C標準の文字どおりの解釈に応じて)正しいです。問題は、構造体内に新しい(空の)列挙定義を作成したことです。以前の定義EnumDefは(意図したとおりに)使用していません。

typdefを使用すると、同様のタイプのタイプミスにより、不明なタイプを使用するとコンパイラエラーが発生します。

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

構造体と列挙型を常にtypedefすることを推奨します。

タイピングを節約するためだけでなく(意図的なしゃれはありません;))、それがより安全だからです。


1
さらに悪いことに、タイプミスが別のタグと一致する場合もあります。構造体の場合、これによりプログラム全体が正しくコンパイルされ、実行時の動作が未定義になる可能性があります。
MM

3
この定義: 'typedef {FIRST_ITEM、SECOND_ITEM} EnumDef;' 列挙型を定義していません。私は何百もの巨大なプログラムを書いており、他の人が書いたプログラムのメンテナンスを実行するという不運がありました。難しい経験から、構造体でtypedefを使用しても問題が発生するだけです。うまくいけば、プログラマーは、構造体インスタンスを宣言するときに完全な定義を入力するのに問題があるほど障害者ではありません。CはBasicではないので、さらにいくつかの文字を入力しても、プログラムの動作に悪影響はありません。
user3629249

2
絶対的な最小文字数を超えて入力することに嫌悪感を覚える人のために、最小数のキーストロークでアプリケーションを作成しようとするグループに参加することをお勧めします。職場環境、特にピアレビューを厳密に行う職場環境では、彼らの新しい「スキル」を使用しないでください
user3629249

enum多くのコンパイラは「間違った」使用をすると奇妙な警告を発するため、通常、型として使用することはお勧めしません。たとえば、enum0 に初期化すると、「列挙型にない整数定数」の警告が表示される場合があります。を前方宣言するenumこともできません。代わりにint(またはunsigned int)を使用する必要があります。
YYNY

5
その例はコンパイルできませんし、期待もしていません。Debug / test.o test.c:10:17のコンパイル:エラー:フィールドに不完全な型 'enum EnuumDef'が含まれていますenum EnuumDef MyEnum; ^ test.c:10:8:注: 'enum EnuumDef'の前方宣言enum EnuumDef MyEnum; ^ 1個のエラーが発生しました。gnuc、std = c99。
natersoz 2017年

30

Linuxカーネルコーディングスタイルの第5章では、を使用する場合の長所と短所(主に短所)を示していtypedefます。

「vps_t」などは使用しないでください。

構造体とポインタにtypedefを使用するのは誤りです。あなたが見たとき

vps_t a;

ソースでは、それはどういう意味ですか?

対照的に、それが言うなら

struct virtual_container *a;

あなたは実際に「a」が何であるかを言うことができます。

多くの人々は、typedefは「読みやすさを助ける」と考えています。そうではありません。これらは、次の場合にのみ役立ちます。

(a)完全に不透明なオブジェクト(typedefはオブジェクトを非表示にするためにアクティブに使用されます)。

例: "pte_t"など。適切なアクセサ関数を使用しないとアクセスできない不透明なオブジェクト。

注意!不透明度と「アクセサ機能」はそれ自体では良くありません。pte_tなどのようなもののためにそれらを持っている理由は、そこに移植可能なアクセス可能な情報が絶対にゼロであるためです。

(b)明確な整数型。抽象化、それが「int」であるか「long」であるかの混乱回避するのに役立ちます

u8 / u16 / u32は完全にすばらしいtypedefですが、ここよりもカテゴリ(d)に適しています。

注意!繰り返しますが、これには理由が必要です。「署名されていない長い」ものがある場合、行う理由はありません

typedef unsigned long myflags_t;

しかし、特定の状況下で「unsigned int」であり、他の構成では「unsigned long」になる理由が明確な場合は、必ず先に進んでtypedefを使用してください。

(c)スパースを使用して文字通り型チェック用の新しい型を作成する場合。

(d)特定の例外的な状況において、標準のC99タイプと同一の新しいタイプ。

目と脳が 'uint32_t'のような標準型に慣れるのにほんの少しの時間しかかかりませんが、一部の人々はとにかくその使用に反対します。

したがって、Linux固有の「u8 / u16 / u32 / u64」タイプと、標準タイプと同じである署名付きの同等物が許可されます。ただし、独自の新しいコードでは必須ではありません。

タイプのセットのいずれかをすでに使用している既存のコードを編集するときは、そのコードの既存の選択に準拠する必要があります。

(e)ユーザースペースで使用しても安全なタイプ。

ユーザー空間に表示される特定の構造では、C99タイプを要求できず、上記の 'u32'形式を使用できません。したがって、ユーザースペースと共有されるすべての構造で__u32および類似のタイプを使用します。

他のケースもあるかもしれませんが、これらのルールの1つに明確に一致することができない限り、基本的にtypedefを決して使用しないようにする必要があります。

一般に、ポインタ、または合理的に直接アクセスできる要素を持つ構造体は、typedefにしないでください。


4
「不透明性と「アクセサ機能」はそれ自体では良くありません」。誰かが理由を説明できますか?情報の隠蔽とカプセル化は非常に良い考えだと思います。
Yawar

5
@ヤワル私はこのドキュメントを読んだだけで、まったく同じ考えでした。もちろん、Cはオブジェクト指向ではありませんが、抽象化はまだ重要です。
Baldrickk 2017年

12

長所と短所があることがわかります。有用な情報源は、精力的な本「エキスパートCプログラミング」(第3章)です。簡単に言うと、Cには複数の名前空間があります。タグ、タイプ、メンバー名、識別子です。typedef型のエイリアスを導入し、それをタグ名前空間に配置します。つまり、

typedef struct Tag{
...members...
}Type;

2つのことを定義します。タグ名前空間の1つのタグとタイプ名前空間の1つのタイプ。したがって、Type myTypeとの両方を実行できますstruct Tag myTagTypestruct Type myTypeまたはのような宣言Tag myTagTypeは違法です。さらに、次のような宣言では:

typedef Type *Type_ptr;

Typeへのポインタを定義します。したがって、次のように宣言したとします。

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

次にvar1var2およびmyTagType1TypeへのポインタmyTagType2ですが、そうではありません。

上記の本の中で、typedefing構造体はプログラマが単語structを書く手間を省くだけなのであまり有用ではないと述べています。ただし、他の多くのCプログラマーと同様に、私は反対意見を持っています。Cでポリモーフィズムを実装する場合、一部の名前が難読化されることがあります(そのため、カーネルなどの大規模なコードベースではお勧めできません)。詳細については、こちらを参照してください。例:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

できるよ:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

したがって、外部メンバー(flagsMyPipe、キャストを通じて内部構造体()によって)に。私にとっては、その(struct MyWriter_ *) s;ような機能を実行するたびに行うよりも、型全体をキャストする方が混乱が少ないです。このような場合、コード内でこの手法を多用する場合は特に、簡単な参照が重要になります。

最後に、typedefed型の最後の側面は、マクロとは対照的に、それらを拡張できないことです。たとえば、次の場合:

#define X char[10] or
typedef char Y[10]

次に宣言できます

unsigned X x; but not
unsigned Y y;

構造体の場合、ストレージ指定子(volatileおよびconst)には適用されないため、これについては特に気にしません。


1
MyPipe *s; MyWriter *self = (MyWriter *) s;そして、あなたは厳密なエイリアシングを壊しました。
Jonathon Reinhart、2015年

@JonathonReinhartこれを回避する方法を言及することは実例です。たとえば、非常にキャストハッピーなGTK +がそれを回避する方法:bugzilla.gnome.org/show_bug.cgi ?id=140722
underscore_d

「typedefは型のエイリアスを導入し、それをタグの名前空間に配置します。つまり、「 typedef struct Tag{ ...members... }Type; 2つのことを定義する」はまったく意味がありません。typedefがタグを定義する場合、ここでの「タイプ」もタグでなければなりません。真実は(。確かまたは2種類と1個のタグではない)の定義は2個のタグや1型を定義している:struct TagTagTypestruct Tag間違いなくタイプです。Tagタグです。しかし、混乱はTypeタグなのかタイプなのか
Qwertyzw

12

typedefを使用して前方宣言を行うこともできません。構造体、列挙型、および共用体を使用すると、依存関係(について知っている)が双方向である場合に宣言を転送できます。

スタイル:C ++でのtypedefの使用はかなり理にかなっています。これは、複数のパラメータや変数パラメータを必要とするテンプレートを扱う場合にほとんど必要になる可能性があります。typedefは命名をまっすぐに保つのに役立ちます。

Cプログラミング言語ではそうではありません。typedefの使用は、ほとんどの場合、データ構造の使用を難読化する以外に目的はありません。{struct(6)、enum(4)、union(5)}の数のキーストロークのみがデータ型の宣言に使用されるため、structのエイリアシングはほとんど使用されません。そのデータ型は共用体または構造体ですか?型定義されていない単純な宣言を使用すると、その型がすぐにわかります。

Linuxがこのエイリアシングを厳密に回避してどのように書かれているかに注意してください。結果は、ミニマリストでクリーンなスタイルです。


10
クリーンはstructどこでも繰り返されないでしょう... Typedefは新しいタイプを作ります。あなたは何を使うのですか?タイプ。私たちはしていない気にそれは構造体、共用体、または列挙型かどうなぜそれtypedefのことです。
GManNickG 2010年

13
いいえ、それ構造体か共用体か、列挙型か何らかのアトミック型か気にしません。構造体を整数またはポインタ(または、他の任意の型)に強制することはできません。これは、コンテキストを格納するために必要なすべてです。「struct」または「union」キーワードを使用すると、推論の局所性が向上します。誰もあなたが何を知っておく必要があると言うん構造体。
Bernd Jendrissek 2012年

1
@BerndJendrissek:構造体と共用体は他のタイプとは異なりますが、クライアントコードはこれらの2つのもの(構造体または共用体)のFILEどれがaのようなものであるかを気にする必要がありますか?
スーパーキャット2015年

4
@supercat FILEはtypedefの良い使い方です。typedefは過度に使用されていると思いますが、それは言語の機能の誤りではありません。すべてにtypedefを使用する私見は、「投機的過大性」コードのにおいです。変数をFILE * fooとして宣言するのではなく、FILE fooとして宣言することに注意してください。私にとって、これは重要です。
Bernd Jendrissek

2
@supercat:「ファイル識別変数のタイプがFILE *ではなくFILEである場合...」しかし、それがtypedefによって可能になるあいまいさです。FILE *を取ってfopenするのに慣れているので、心配することはありませんが、typedefを追加するたびに、認識オーバーヘッドのさらに別のビットを導入しています。このAPIはfoo_t argsまたはfoo_t *を必要としますか?「構造体」を明示的に実行すると、関数定義あたりの文字数が増える代わりに、推論の局所性が向上します。
Bernd Jendrissek 2015年

4

基本から始めましょう。

構造定義の例を次に示します。

struct point
  {
    int x, y;
  };

ここでは、名前pointはオプションです。

構造体は、その定義中または後に宣言できます。

定義中の宣言

struct point
  {
    int x, y;
  } first_point, second_point;

定義後の宣言

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

ここで、上記の最後のケースに注意してください。struct pointコードの後半でその型を作成する場合は、その型の構造体を宣言するように記述する必要があります。

と入力しtypedefます。プログラムで同じブループリントを使用して後で新しい構造(構造はカスタムデータ型)を作成する場合は、typedefその定義中に使用することをお勧めします。

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

カスタムタイプに名前を付ける際の注意事項

カスタムタイプ名の末尾に_tサフィックスを使用することを妨げるものは何もありませんが、POSIX標準では、標準ライブラリタイプ名を示すためにサフィックス_tの使用を予約しています。


3

構造体に(オプションで)指定する名前はタグ名と呼ばれ、前述のとおり、それ自体は型ではありません。タイプに到達するには、構造体の接頭辞が必要です。

GTK +は別として、タグ名がstruct型のtypedefと同じくらい一般的に使用されているかどうかはわかりません。そのため、C ++では認識され、structキーワードを省略して、タグ名を型名として使用することもできます。


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;


1

typedefは、相互依存するデータ構造のセットを提供しません。これはtypdefでは行えません:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

もちろん、いつでも追加できます。

typedef struct foo foo_t;
typedef struct bar bar_t;

それの正確なポイントは何ですか?


1

A> typdefは、データ型のより意味のある同義語の作成を許可することにより、プログラムの意味とドキュメント化を支援します。さらに、移植性の問題(K&R、pg147、C prog lang)に対するプログラムのパラメーター化を支援します。

B> 構造体はタイプを定義します。Structsを使用すると、varのコレクションをグループ化して、単一のユニットとして扱いやすくすることができます(K&R、pg127、Cプログラム言語)。

C>構造体のtypedefについては、上記のAで説明しています。

D>私にとって、構造体はカスタムタイプ、コンテナ、コレクション、名前空間、または複雑なタイプですが、typdefは、より多くのニックネームを作成するための手段にすぎません。


0

C99ではtypedefが必要であることが判明しました。時代遅れですが、多くのツール(HackRankとして)は、純粋なC実装としてc99を使用しています。そして、typedefが必要です。

要件が変更された場合、それらを変更する必要があるとは言っていません(おそらく2つのCオプションがあります)。


2
「C99での退社typedefが必要です。」どういう意味ですか?
ジュリアンロペス

問題は、Cではなく、についてC++です。Ctypedefである「必要な」(最も可能性の高い常になります)。'必須'の場合と同様に、変数を宣言しPoint varName;て型をstruct Point;なしで同義にすることはできませんtypedef struct Point Point;
YYNY

0

'C'プログラミング言語では、キーワード 'typedef'を使用して、オブジェクト(構造体、配列、関数..enum型)の新しい名前を宣言します。たとえば、「struct-s」を使用します。「C」では、「メイン」関数の外で「構造体」を宣言することがよくあります。例えば:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

構造体型を使用することを決定するたびに、このキーワード 'struct' something '' name 'が必要になります。'typedef'はその型の名前を変更するだけで、プログラムでその名前をいつでも使用できます。したがって、コードは次のようになります。

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

プログラム全体で使用されるローカルオブジェクト(構造体、配列、貴重なもの)がある場合は、 'typedef'を使用して名前を付けるだけです。


-2

まったく、C言語では、struct / union / enumはC言語のプリプロセッサによって処理されるマクロ命令です( "#include"などを扱うプリプロセッサと間違えないでください)。

そう :

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

struct bは次のように使用されます。

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

したがって、コンパイル時にスタック上で次のように展開されます:b:int ai int i int j

そのため、自己参照構造体を持つのが難しいのです。Cプリプロセッサは、終了できない宣言ループで丸めます。

typedefは型指定子です。つまり、Cコンパイラのみがそれを処理し、アセンブラコードの実装を最適化するために必要な処理を実行できます。また、préprocessorが構造体で行うようにpar型のメンバーを愚かに消費しませんが、より複雑な参照構築アルゴリズムを使用するため、次のような構築を行います。

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

許可され、完全に機能します。この実装は、コンパイラー型変換へのアクセスも提供し、実行スレッドが初期化関数のアプリケーションフィールドを離れたときのいくつかのバグの影響を取り除きます。

つまり、Cでは、typedefは孤独な構造体よりもC ++クラスに近いということです。


3
自己参照構造体を持つことはまったく難しくありません。struct foo {struct foo * next; intのもの; }
Bernd Jendrissek 2015年

4
...何?とsの解像度を説明するためにプリプロセッサを言うのは十分に悪いですが、あなたの書く残りの部分は非常に混乱しているので、メッセージを取得するのは難しいと思います。ただし、私が言えることの1つは、non - d を前方宣言したり、不透明(ポインター)メンバーとして使用したりすることはできないというあなたの考えは完全に誤りです。最初の例では、を簡単に含めることができますが、必須ではありません。sは単なるマクロ展開のばかばかしい部分であり、sがそれらに革命的な新しい力を与えるという主張は痛々しいほど正しくありませんstructstypedeftypedefstructstruct bstruct a *typedefstructtypedef
underscore_d
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.