考慮してください:
#define MAXROW 20
#define MAXCOL 60
typedef State Grid[MAXROW+2] [MAXCOL+2]
typedef enum state {DEAD,ALIVE} State
Cでtypedef
およびtypedef enum
を使用するにはどうすればよいですか?コードのこの部分は何をしますか?
typedef enum state {DEAD,ALIVE} State;
| | | | | |^ terminating semicolon, required!
| | | type specifier | | |
| | | | ^^^^^ declarator (simple name)
| | | |
| | ^^^^^^^^^^^^^^^^^^^^^^^
| |
^^^^^^^-- storage class specifier (in this case typedef)
typedef
キーワードは、疑似ストレージクラス指定子です。構文的には、extern
やstatic
などのストレージクラス指定子が使用されるのと同じ場所で使用されます。ストレージとは何の関係もありません。これは、宣言が名前付きオブジェクトの存在を導入しないことを意味しますが、むしろtype aliasesの名前を導入します。
上記の宣言の後、State
識別子は、タイプenum state {DEAD,ALIVE}
のエイリアスになります。宣言は、その型自体も提供します。ただし、それはtypedef
ではありません。 Anyenum state {DEAD,ALIVE}
が型指定子として現れる宣言は、その型をスコープに導入します:
enum state {DEAD, ALIVE} stateVariable;
enum state
が以前に導入されている場合は、typedef
を次のように記述する必要があります。
typedef enum state State;
そうでない場合、enum
が再定義されますが、これはエラーです。
他の宣言(関数パラメーター宣言を除く)と同様に、typedef
宣言には、コンマで区切られた複数の宣言子を含めることができます。さらに、単純な名前だけでなく、派生した宣言子にすることもできます。
typedef unsigned long ulong, *ulongptr;
| | | | | 1 | | 2 |
| | | | | | ^^^^^^^^^--- "pointer to" declarator
| | | | ^^^^^^------------- simple declarator
| | ^^^^^^^^^^^^^-------------------- specifier-qualifier list
^^^^^^^---------------------------------- storage class specifier
このtypedef
は、指定子修飾子リストで指定されたunsigned long
型に基づいて、2つの型名ulong
およびulongptr
を導入します。 ulong
は、その型の単なるエイリアスです。 ulongptr
は、unsigned long
構文のおかげで、*
へのポインターとして宣言されます。この構文は、式で使用されるポインター逆参照用の単項*
を意図的に模倣する一種の型構築演算子です。つまり、ulongptr
は「unsigned long
へのポインター」型のエイリアスです。
エイリアスとは、ulongptr
特殊タイプではないunsigned long *
からの意味です。これは有効なコードであり、診断は不要です。
unsigned long *p = 0;
ulongptr q = p;
変数q
とp
の型はまったく同じです。
typedef
のエイリアシングはテキストではありません。たとえば、user_id_t
がtypedef
型のint
名である場合、単純にこれを行うことはできません。
unsigned user_id_t uid; // error! programmer hoped for "unsigned int uid".
これは、unsigned
とtypedef名を組み合わせた無効な型指定子リストです。上記は、Cプリプロセッサを使用して実行できます。
#define user_id_t int
unsigned user_id_t uid;
これにより、user_id_t
は、構文分析および変換の前に、トークンint
にマクロ展開されます。これは利点のように思えるかもしれませんが、それは間違っています。新しいプログラムではこれを避けてください。
派生型ではうまく機能しないという欠点の中で:
#define silly_macro int *
silly_macro not, what, you, think;
この宣言では、what
、you
、およびthink
が「intへのポインター」型であると宣言されていません。マクロ展開が次のようになっているためです。
int * not, what, you, think;
型指定子はint
で、宣言子は*not
、what
、you
、およびthink
です。したがって、not
には期待されるポインター型がありますが、残りの識別子にはありません。
そして、それはおそらく、typedef
とCでの型エイリアスについてのすべての99%です。
typedef
は、新しいデータ型を定義します。だからあなたは持つことができます:
typedef char* my_string;
typedef struct{
int member1;
int member2;
} my_struct;
したがって、これらの新しいデータ型を使用して変数を宣言できます
my_string s;
my_struct x;
s = "welcome";
x.member1 = 10;
enum
の場合、状況は少し異なります。次の例を検討してください。
enum Ranks {FIRST, SECOND};
int main()
{
int data = 20;
if (data == FIRST)
{
//do something
}
}
typedef enum
を使用すると、タイプのエイリアスが作成されます。
typedef enum Ranks {FIRST, SECOND} Order;
int main()
{
Order data = (Order)20; // Must cast to defined type to prevent error
if (data == FIRST)
{
//do something
}
}
補足だけ:
6.7.8型定義
Typedef宣言は新しいタイプを導入せず、指定されたタイプの同義語のみを導入します。
open-std.org ISO/IEC 9899:2017
typedef
がCで新しいデータ型を作成することに同意する人々は、free()
関数を使用し、名前へのすべての参照をNULL
に設定します。