typedef struct による構造体の定義|一般的な宣言との違いや目的

typedef structによる構造体の定義

構造体はtypedefと組み合わせて定義することがあります。

typedef struct Number_ {
   int value;
} Number;

typedef指定子で構造体struct Number_の別名Numberを定義しています。これによって、変数宣言時にstructキーワードの記述が不要になります。

int main() {
   Number object; // structキーワードが不要
   struct Number_ object; // タグ名の場合はstructキーワードが必須
}

目次

structによる構造体の定義との違い

typedefを用いなかった場合には、変数宣言時にstructキーワードの指定が必須となります。

struct Number_ { int value; };

int main() {
   struct Number_ object; // structが必須、省略不可
}

多くのケースでは、このstructキーワードの記述を省く目的として、typedefによる別名を活用することがほとんどです。

// Before
struct Number add(struct Number a, struct Number b);
// After
Number add(Number a, Number b);

typedef structによる定義の仕組み

typedef struct Number_ { int value; } Number;

typedefによる構造体の定義方法は見慣れない記法かもしれませんが、仕組み自体は単純です。そもそもtypedef指定子は型の別名を定義する機能なのですが、これがtypedef 型 別名という宣言方法になっています。

そして今回上記で紹介した構造体の定義方法は、このの部分に構造体定義のための構文を直接指定する形となっています。人によっては以下のように書いたほうが最初は覚えやすいかもしれません。やっていることは同じです。

struct Number_ { int value; }; // 構造体の定義
typedef struct Number_ Number; // 別名の定義

ちなみにtypedefで定義した別名に対してstructキーワードを指定することはできません。

typedef struct Number_ {} Number;
Number object;        // OK
struct Number object; // error: tentative definition has type 'struct Number' that is never completed
                      // note: forward declaration of 'struct Number'

匿名構造体に対するtypedef

ちなみに構造体のタグ名は省略することができるため、以下のような別名のみの定義も可能です。

typedef struct {
   int value;
} Number;

typedefによる別名と構造体のタグ名は同じでも良い

構造体のタグ名とtypedefによる別名には同じ名前を用いることができます。変数宣言時のstructキーワードで両者を区別することができるためです。

typedef struct Number {} Number;

struct Number a; // 構造体のタグ名で宣言
Number b;        // typedefで定義された別名で宣言

ただし実際の開発では、構造体の定義時にはタグ名を省略したり、タグ名と別名を区別したりするのが良いでしょう。名前の重複や余計な型名が増えると、IDE側の機能が若干使いづらくなる場合があります。

EclipseのOpen Declaration (F3) 機能やXcodeのJump To Definition (^⌘J) 機能などがその良い例。構造体のタグ名とtypedefによる別名が同名だと、ジャンプ先の候補リストが表示されてしまう場合がある。名前重複が起こらなければ、定義箇所へのジャンプが一発で行えるようになる

アンダースコアが用いられた構造体について

APIやフレームワークでは、タグ名による変数宣言(struct Number object;)を行わせないために、アンダースコアを用いた命名を行うことがあります(struct Number_, struct __CFString)。この手の命名は暗黙的にプライベートな識別子であることを示唆している場合が多いため、基本的には別名による宣言(Number object;)を用いる必要があります。他にも、構造体の内部構造を隠蔽したり、ポインタとしての宣言を強制させるために、このような命名が取られることがあります。

ちなみにアンダースコアで始まる命名やダブルアンダースコアが用いられた命名は予約済みとなっているため、ユーザ定義の型名にこの手の命名を用いることはできません。末尾に一文字のアンダースコアを用いることをオススメします(可: struct Number_, 不可: struct _Number, ローカルスコープでは可: struct _number

広告