以下のような構造で構成される多くのプログラムを見てきました
typedef struct
{
int i;
char k;
} elem;
elem user;
なぜ頻繁に必要なのですか?具体的な理由や該当する分野はありますか?
Greg Hewgill が言ったように、typedef を使用すると、あちこちに書き込む必要がなくなりますstruct
。これはキーストロークを節約するだけでなく、コードをより簡潔にすることもできます。
のようなもの
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;
}
この後者の場合、Point by 値を返すことはできません。これは、その定義がヘッダー ファイルのユーザーから隠されているためです。これは、たとえばGTK+で広く使用されている手法です。
更新typedef
この非表示の使用が悪い考えと見なされる、高く評価されている C プロジェクトもあることに注意してくださいstruct
。Linux カーネルは、おそらく最もよく知られているそのようなプロジェクトです。Linus の怒りの言葉については、 The Linux Kernel CodingStyle ドキュメントの第 5 章を参照してください。:) 私のポイントは、結局のところ、質問の「すべき」はおそらく決まっていないということです。
これを勘違いしている人が驚くほど多いです。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 を使用せずに、コンパイル ユニットが foo.h をインクルードしてFOO_DEF
定義を取得することができます。構造体の「bar」メンバーを逆参照しようとしない場合foo
は、「bar.h」ファイルを含める必要はありません。
また、タグ名とメンバー名では名前空間が異なるため、次のような非常に読みやすいコードを書くことができます。
struct foo *foo;
printf("foo->bar = %p", foo->bar);
名前空間は分離されているため、構造体タグ名と一致する変数の名前付けに競合はありません。
コードを保守する必要がある場合は、typedef の構造体を削除します。
Dan Saks による古い記事 ( http://www.ddj.com/cpp/184403396?pgno=3 ) から:
構造体の命名に関する C 言語の規則は少し風変わりですが、ほとんど害はありません。ただし、C++ のクラスに拡張すると、これらの同じ規則が小さな亀裂を生じ、バグが這い回ることができます。
C では、名前は
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 を宣言する有効な宣言です。良い習慣ではないかもしれませんが、C コンパイラはそれを受け入れなければなりません。Cがこのように設計された理由について、私はこれまで見たことがありません。これは間違いだとずっと思っていましたが、そうでした。
多くのプログラマー (あなたのプログラマーも含めて) は、構造体名を型名と見なすことを好むため、typedef を使用してタグのエイリアスを定義します。たとえば、
struct s { ... }; typedef struct s S;
次のように、 struct の代わりに S を使用できます。
S x; S *p;
プログラムは、型と変数 (または関数または列挙定数) の両方の名前として S を使用できません。
S S; // error
これはいい。
構造体、共用体、または列挙型定義のタグ名はオプションです。多くのプログラマーは、次のように構造体定義を typedef に折りたたみ、タグを完全に省きます。
typedef struct { ... } S;
リンクされた記事には、 を必要としない C++ の動作がtypedef
微妙な名前の非表示の問題を引き起こす可能性についても説明されています。これらの問題を回避するためにtypedef
、C++ のクラスと構造体にもこれを使用することをお勧めします。一見不要に見えますが。C++ では、typedef
潜在的な問題の隠れた原因ではなく、コンパイラが通知するエラーになります。
a を使用すると、その型の変数を宣言するたびtypedef
に書き込む必要がなくなります。struct
struct elem
{
int i;
char k;
};
elem user; // compile error!
struct elem user; // this is correct
常に typedef 列挙型と構造体を使用するもう 1 つの正当な理由は、この問題に起因します。
enum EnumDef
{
FIRST_ITEM,
SECOND_ITEM
};
struct StructDef
{
enum EnuumDef MyEnum;
unsigned int MyVar;
} MyStruct;
構造体 ( Enu u mDef) の EnumDef のタイプミスに気付きましたか? これはエラー (または警告) なしでコンパイルされ、(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) */
私は常に構造体と列挙型を型定義することを提唱します。
タイピングを節約するためだけでなく (しゃれは意図していません ;))、より安全だからです。
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) に適合します。
ノート!繰り返しますが、これには理由が必要です。何かが「unsigned long」である場合、実行する理由はありません
typedef unsigned long myflags_t;
ただし、特定の状況下では「unsigned int」になり、他の構成では「unsigned long」になる明確な理由がある場合は、必ず先に進んで typedef を使用してください。
(c) sparse を使用して、文字通り型チェック用の新しい型を作成する場合。
(d) 特定の例外的な状況で、標準の C99 型と同一である新しい型。
uint32_t のような標準的な型に目と脳が慣れるまでには、ほんの少しの時間しかかかりませんが、とにかくその使用に反対する人もいます。
したがって、Linux 固有の 'u8/u16/u32/u64' 型と、標準型と同一の符号付きの同等のものは許可されていますが、独自の新しいコードでは必須ではありません。
いずれかのタイプのセットをすでに使用している既存のコードを編集する場合は、そのコードの既存の選択に従う必要があります。
(e) ユーザー空間で安全に使用できる型。
ユーザー空間に表示される特定の構造では、C99 型を要求できず、上記の「u32」形式を使用できません。したがって、ユーザー空間と共有されるすべての構造体で __u32 および類似の型を使用します。
他のケースもあるかもしれませんが、ルールは基本的に、それらのルールのいずれかに明確に一致しない限り、決して typedef を使用しないことです。
一般に、ポインター、または合理的に直接アクセスできる要素を持つ構造体は、決してtypedef にしないでください。
typedefで前方宣言さえ可能だとは思いません。struct、enum、および union を使用すると、依存関係 (知っている) が双方向である場合に宣言を転送できます。
スタイル: C++ での typedef の使用はかなり理にかなっています。複数のパラメータや可変パラメータを必要とするテンプレートを扱う場合、ほとんど必要になる可能性があります。typedef は、命名をまっすぐに保つのに役立ちます。
Cプログラミング言語ではそうではありません。typedef の使用は、ほとんどの場合、データ構造の使用法を難読化する以外には何の目的もありません。{ struct (6), enum (4), union (5) } 数のキーストロークのみがデータ型の宣言に使用されるため、構造体のエイリアシングはほとんど使用されません。そのデータ型はユニオンまたは構造体ですか? 単純な型定義されていない宣言を使用すると、それがどの型であるかをすぐに知ることができます。
typedef がもたらす意味のないエイリアシングを厳密に回避して Linux が書かれていることに注目してください。その結果、ミニマリストでクリーンなスタイルが完成しました。
基本から始めて、上に向かって進んでいきましょう。
構造定義の例を次に示します。
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 の使用が予約されています。
(オプションで) 構造体に付ける名前はタグ名と呼ばれ、前述のように、それ自体は型ではありません。型を取得するには、構造体の接頭辞が必要です。
GTK+ はさておき、タグ名が構造体型の typedef と同じくらい一般的に使用されているかどうかはわかりません。そのため、C++ ではそれが認識され、構造体キーワードを省略してタグ名を型名として使用することもできます。
struct MyStruct
{
int i;
};
// The following is legal in C++:
MyStruct obj;
obj.i = 7;
「C」プログラミング言語では、キーワード「typedef」を使用して、オブジェクト (構造体、配列、関数..列挙型) の新しい名前を宣言します。たとえば、「struct-s」を使用します。「C」では、「main」関数の外で「struct」を宣言することがよくあります。例えば:
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' を使用して名前を付けることができます。
C99 では typedef が必要であることが判明しました。時代遅れですが、多くのツール (ala HackRank) は純粋な C 実装として c99 を使用しています。そこには typedef が必要です。
要件が変更された場合に変更する必要があると言っているわけではありません (おそらく 2 つの C オプションが必要です)。
そもそも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 コンパイラのみがそれを処理し、アセンブラー コードの実装を最適化するために必要なように処理できます。また、プリプロセッサが構造体で行うように型 par のメンバーを愚かに消費することはありませんが、より複雑な参照構築アルゴリズムを使用するため、次のような構築:
typedef struct a A; //anticipated declaration for member declaration
typedef struct a //Implemented declaration
{
A* b; // member declaration
}A;
許可され、完全に機能します。この実装は、コンパイラの型変換へのアクセスも提供し、実行スレッドが初期化関数のアプリケーション フィールドを離れるときのバグの影響を取り除きます。
これは、C では typedef が単独の構造体よりも C++ クラスに近いことを意味します。