私はCプログラミングの初心者ですが、構造体を定義するときにtypedef
を使用することとtypedef
を使用しないことの違いは何だろうと思いました。私には本当に違いはないようです、彼らは同じ目標を達成しています。
struct myStruct{
int one;
int two;
};
vs.
typedef struct{
int one;
int two;
}myStruct;
一般的な慣用句では、両方を使用します。
typedef struct X {
int x;
} X;
それらは異なる定義です。議論をより明確にするために、文章を分割します。
struct S {
int x;
};
typedef struct S S;
最初の行では、構造体名前空間内で識別子S
を定義しています(C++の意味ではありません)。これを使用して、引数の型をstruct S
として定義することによって、新しく定義された型の変数または関数引数を定義できます。
void f( struct S argument ); // struct is required here
2行目では、グローバル名前空間に型エイリアスS
を追加しているので、次のように書くだけです。
void f( S argument ); // struct keyword no longer needed
両方の識別子の名前空間は異なるので、構造体とグローバル空間の両方でS
を定義しても、同じ識別子を再定義するのではなく、別の場所に別の識別子を作成するのでエラーにはなりません。
違いを明確にするために:
typedef struct S {
int x;
} T;
void S() { } // correct
//void T() {} // error: symbol T already defined as an alias to 'struct S'
識別子が異なるスペースに保持されるのと同じ構造体の名前で関数を定義できますが、それらの識別子が衝突するため、typedef
と同じ名前で関数を定義することはできません。
C++では、シンボルを見つけるための規則が微妙に変更されているため、若干異なります。 C++は2つの異なる識別子空間を保持しますが、Cとは異なり、シンボルをクラス識別子空間内にのみ定義する場合は、struct/classキーワードを指定する必要はありません。
// C++
struct S {
int x;
}; // S defined as a class
void f( S a ); // correct: struct is optional
変更点は検索規則であり、識別子が定義されている場所ではありません。コンパイラはグローバル識別子テーブルを検索し、S
が見つからないと、クラス識別子内でS
を検索します。
前に示したコードは同じように動作します。
typedef struct S {
int x;
} T;
void S() {} // correct [*]
//void T() {} // error: symbol T already defined as an alias to 'struct S'
2行目でS
関数を定義した後は、構造体Sをコンパイラで自動的に解決することはできません。そのため、オブジェクトを作成したりその型の引数を定義したりする場合は、struct
キーワードを含める必要があります。
// previous code here...
int main() {
S();
struct S s;
}
struct
name__とtypedef
name__は、2つの非常に異なるものです。
struct
name__キーワードは、構造体型を定義または参照するために使用されます。たとえば、
struct foo {
int n;
};
struct foo
という新しい型を作成します。 foo
name__という名前は タグ です。タグと他の識別子は 名前空間 に区別されるので、その前にstruct
name__キーワードがある場合にのみ意味があります。 (これは、C++のnamespace
name__sの概念に似ていますが、それよりはるかに制限されています。)
名前にもかかわらず、typedef
name__は新しい型を定義しません。単に既存の型に新しい名前をつけるだけです。例えば、
typedef int my_int;
my_int
はint
name__の新しい名前です。 my_int
とint
name__は、 正確に 同じタイプです。同様に、上記のstruct
name__の定義を考えると、次のように書くことができます。
typedef struct foo foo;
型はすでにstruct foo
という名前を持っています。 typedef
name__宣言は、同じ型に新しい名前foo
name__を付けます。
この構文により、struct
name__とtypedef
name__を単一の宣言にまとめることができます。
typedef struct bar {
int n;
} bar;
これは一般的な慣用句です。これで、この構造体型をstruct bar
または単にbar
name__として参照できます。
Typedef名は宣言が終わるまで表示されません。構造体にそれ自身へのポインタが含まれている場合は、それを参照するためにstruct
name__バージョンを使用しています。
typedef struct node {
int data;
struct node *next; /* can't use just "node *next" here */
} node;
一部のプログラマは、structタグとtypedef名に異なる識別子を使用します。私の意見では、その理由はありません。同じ名前を使用することは完全に合法であり、それらが同じタイプであることを明確にします。異なる識別子を使用する必要がある場合は、少なくとも一貫した規則を使用してください。
typedef struct node_s {
/* ... */
} node;
(個人的には、typedef
name__を省略し、struct bar
として型を参照することをお勧めします。typedef
name__は、型付けを少し節約することができますが、構造体型であるという事実を隠します。クライアントコードがメンバn
name__を名前で参照するのであれば、それは不透明ではなく、見かけ上は構造体であり、私の意見ではそれを構造体として参照するのは理にかなっています。どちらの方法で書かれたコードも読んで理解する準備をしてください。)
(C++にはさまざまな規則があります。struct blah
の宣言を考えると、typedefがなくても、単にblah
name__として参照することができます。typedefを使用すると、Cコードがもう少しC++になる可能性があります。 。)
指摘されていないもう1つの違いは、構造体に名前(つまり、struct myStruct)を付けることでも、構造体の前方宣言を提供できるようになることです。そのため、他のファイルでは、次のように書くことができます。
struct myStruct;
void doit(struct myStruct *ptr);
定義にアクセスする必要はありません。私がお勧めすることはあなたがあなたの2つの例を組み合わせることです:
typedef struct myStruct{
int one;
int two;
} myStruct;
これにより、より簡潔なtypedef名の利便性が得られますが、必要に応じて完全な構造体名を使用することもできます。
C(C++以外)では、次のような構造体変数を宣言する必要があります。
struct myStruct myVariable;
代わりにmyStruct myVariable;
を使用できるようにするには、構造体をtypedef
にします。
typedef struct myStruct someStruct;
someStruct myVariable;
struct
の定義とそれをtypedef
sとを匿名のstruct
とtypedef
とを宣言する単一のステートメントにまとめることができます。
typedef struct { ... } myStruct;
struct
を指定せずにtypedef
を使用する場合は、必ず次のように書く必要があります。
struct mystruct myvar;
書くのは違法です
mystruct myvar;
あなたがtypedef
を使うならば、あなたはもうstruct
接頭辞を必要としません。
Cでは、構造体、共用体、列挙型の型指定子キーワードは必須です。つまり、型を参照するときは必ず型の名前( tag )の前にstruct
、union
またはenum
を付ける必要があります。
typedef
を使用することでキーワードを取り除くことができます。これは、オブジェクトの実際の型が宣言時に表示されなくなるため、情報隠蔽の形式です。
そのため、実際に want を使用する場合にのみ、これを実行することをお勧めします(たとえば、 Linuxカーネルコーディングスタイルガイド 、第5章を参照)。
いつtypedef
を使用すべきかの例は、対応するアクセサ関数/マクロでしか使用されていない不透明(OPAQUE)型です。
typedef struct
と一緒に前方宣言を使用することはできません。
struct
自体は匿名型なので、前方宣言するための実際の名前はありません。
typedef struct{
int one;
int two;
} myStruct;
このような前方宣言は機能しません。
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
次のコードは、エイリアスmyStruct
を持つ無名構造体を作成します。
typedef struct{
int one;
int two;
} myStruct;
構造体の識別子を指定していないため、別名なしで参照することはできません。
違いはstruct
を使うときに現れます。
あなたがしなければならない最初の方法:
struct myStruct aName;
2番目の方法では、キーワードstruct
を削除できます。
myStruct aName;
typedef
は、他のコンストラクトと同様に、データ型に新しい名前を付けるために使用されます。この場合それはコードをきれいにするために大抵行われます:
struct myStruct blah;
vs.
myStruct blah;
これについては明確な説明があります。 CとC++は型の定義方法が異なります。 C++はもともとCの上にある追加のインクルードセットにすぎませんでした。
ほとんどすべてのC/C++開発者が今日抱えている問題は、a)大学がもはや基礎を教えていないこと、そしてb)人々が定義と宣言の違いを理解していないことです。
そのような宣言と定義が存在する唯一の理由は、リンカが構造内のフィールドへのアドレスオフセットを計算できるようにするためです。これが、ほとんどの人が実際には正しく書かれていないコードを使用しない理由です。コンパイラがアドレッシングを決定できるからです。問題は、キュー、リンクリスト、またはO/S構造のピギーバックなど、何かを進めようとしたときに発生します。
宣言は 'struct'で始まり、定義は 'typedef'で始まります。
さらに、構造体には前方宣言ラベルと定義済みラベルがあります。ほとんどの人はこれを知らず、前方宣言ラベルを定義ラベルとして使用します。
違う:
struct myStruct
{
int field_1;
...
};
彼らは構造体にラベルを付けるために前方宣言を使用しました - だから今コンパイラはそれを認識しています - しかしそれは実際に定義された型ではありません。コンパイラはアドレッシングを計算することができます - しかし、これはそれが使用されることを意図した方法ではありません。
この形式の宣言を使用する人々は、事実上すべての参照に常に 'struct'を入れる必要があります - それは公式の新しいタイプではないためです。
代わりに、それ自体を参照しない構造は、この方法でのみ宣言および定義されるべきです。
typedef struct
{
field_1;
...
}myStruct;
今ではそれは実際の型です、そして使用されるときあなたはそれにWordの 'struct'を付加する必要なしに 'myStruct'として使用することができます。
その構造体へのポインタ変数が必要な場合は、二次ラベルを含めます。
typedef struct
{
field_1;
...
}myStruct,*myStructP;
これで、その構造へのポインタ変数ができました。
前方宣言 -
さて、これが派手なもの、前方宣言がどのように機能するかです。リンクリストやキュー要素など、自分自身を参照する型を作成する場合は、前方宣言を使用する必要があります。コンパイラは、最後にセミコロンに達するまで、定義された構造を考慮しません。したがって、その時点で宣言されるだけです。
typedef struct myStructElement
{
myStructElement* nextSE;
field_1;
...
}myStruct;
さて、コンパイラは、型全体がまだ何なのかわからないが、前方参照を使用してそれを参照できることを認識しています。
構造体を正しく宣言してtypedefしてください。実際には理由があります。
後者の例では、構造体を使用するときにstructキーワードを省略します。それで、あなたのコードのどこでも、あなたは書くことができます:
myStruct a;
の代わりに
struct myStruct a;
これは入力の手間を省き、読みやすくなるかもしれませんが、これは好みの問題です。