web-dev-qa-db-ja.com

typedef構造体と構造体定義

私はCプログラミングの初心者ですが、構造体を定義するときにtypedefを使用することとtypedefを使用しないことの違いは何だろうと思いました。私には本当に違いはないようです、彼らは同じ目標を達成しています。

struct myStruct{
    int one;
    int two;
};

vs.

typedef struct{
    int one;
    int two;
}myStruct;
711
user69514

一般的な慣用句では、両方を使用します。

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;
}

structname__とtypedefname__は、2つの非常に異なるものです。

structname__キーワードは、構造体型を定義または参照するために使用されます。たとえば、

struct foo {
    int n;
};

struct fooという新しい型を作成します。 fooname__という名前は タグ です。タグと他の識別子は 名前空間 に区別されるので、その前にstructname__キーワードがある場合にのみ意味があります。 (これは、C++のnamespacename__sの概念に似ていますが、それよりはるかに制限されています。)

名前にもかかわらず、typedefname__は新しい型を定義しません。単に既存の型に新しい名前をつけるだけです。例えば、

typedef int my_int;

my_intintname__の新しい名前です。 my_intintname__は、 正確に 同じタイプです。同様に、上記のstructname__の定義を考えると、次のように書くことができます。

typedef struct foo foo;

型はすでにstruct fooという名前を持っています。 typedefname__宣言は、同じ型に新しい名前fooname__を付けます。

この構文により、structname__とtypedefname__を単一の宣言にまとめることができます。

typedef struct bar {
    int n;
} bar;

これは一般的な慣用句です。これで、この構造体型をstruct barまたは単にbarname__として参照できます。

Typedef名は宣言が終わるまで表示されません。構造体にそれ自身へのポインタが含まれている場合は、それを参照するためにstructname__バージョンを使用しています。

typedef struct node {
    int data;
    struct node *next; /* can't use just "node *next" here */
} node;

一部のプログラマは、structタグとtypedef名に異なる識別子を使用します。私の意見では、その理由はありません。同じ名前を使用することは完全に合法であり、それらが同じタイプであることを明確にします。異なる識別子を使用する必要がある場合は、少なくとも一貫した規則を使用してください。

typedef struct node_s {
    /* ... */
} node;

(個人的には、typedefname__を省略し、struct barとして型を参照することをお勧めします。typedefname__は、型付けを少し節約することができますが、構造体型であるという事実を隠します。クライアントコードがメンバnname__を名前で参照するのであれば、それは不透明ではなく、見かけ上は構造体であり、私の意見ではそれを構造体として参照するのは理にかなっています。どちらの方法で書かれたコードも読んで理解する準備をしてください。)

(C++にはさまざまな規則があります。struct blahの宣言を考えると、typedefがなくても、単にblahname__として参照することができます。typedefを使用すると、Cコードがもう少しC++になる可能性があります。 。)

109
Keith Thompson

指摘されていないもう1つの違いは、構造体に名前(つまり、struct myStruct)を付けることでも、構造体の前方宣言を提供できるようになることです。そのため、他のファイルでは、次のように書くことができます。

struct myStruct;
void doit(struct myStruct *ptr);

定義にアクセスする必要はありません。私がお勧めすることはあなたがあなたの2つの例を組み合わせることです:

typedef struct myStruct{
    int one;
    int two;
} myStruct;

これにより、より簡潔なtypedef名の利便性が得られますが、必要に応じて完全な構造体名を使用することもできます。

87

C(C++以外)では、次のような構造体変数を宣言する必要があります。

struct myStruct myVariable;

代わりにmyStruct myVariable;を使用できるようにするには、構造体をtypedefにします。

typedef struct myStruct someStruct;
someStruct myVariable;

structの定義とそれをtypedefsとを匿名のstructtypedefとを宣言する単一のステートメントにまとめることができます。

typedef struct { ... } myStruct;
53
Mehrdad Afshari

structを指定せずにtypedefを使用する場合は、必ず次のように書く必要があります。

struct mystruct myvar;

書くのは違法です

mystruct myvar;

あなたがtypedefを使うならば、あなたはもうstruct接頭辞を必要としません。

28
RED SOFT ADAIR

Cでは、構造体、共用体、列挙型の型指定子キーワードは必須です。つまり、型を参照するときは必ず型の名前( tag )の前にstructunionまたはenumを付ける必要があります。

typedefを使用することでキーワードを取り除くことができます。これは、オブジェクトの実際の型が宣言時に表示されなくなるため、情報隠蔽の形式です。

そのため、実際に want を使用する場合にのみ、これを実行することをお勧めします(たとえば、 Linuxカーネルコーディングスタイルガイド 、第5章を参照)。

いつtypedefを使用すべきかの例は、対応するアクセサ関数/マクロでしか使用されていない不透明(OPAQUE)型です。

23
Christoph

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
7
Yochai Timmer

次のコードは、エイリアスmyStructを持つ無名構造体を作成します。

typedef struct{
    int one;
    int two;
} myStruct;

構造体の識別子を指定していないため、別名なしで参照することはできません。

6
Wronski

違いはstructを使うときに現れます。

あなたがしなければならない最初の方法:

struct myStruct aName;

2番目の方法では、キーワードstructを削除できます。

myStruct aName;
5
jjnguy

typedefは、他のコンストラクトと同様に、データ型に新しい名前を付けるために使用されます。この場合それはコードをきれいにするために大抵行われます:

struct myStruct blah;

vs.

myStruct blah;
4
RC.

これについては明確な説明があります。 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してください。実際には理由があります。

3
CodeGuru

後者の例では、構造体を使用するときにstructキーワードを省略します。それで、あなたのコードのどこでも、あなたは書くことができます:

myStruct a;

の代わりに

struct myStruct a;

これは入力の手間を省き、読みやすくなるかもしれませんが、これは好みの問題です。

2
shodanex