web-dev-qa-db-ja.com

Cでtypedefとtypedef enumを使用するにはどうすればよいですか?

考慮してください:

#define MAXROW 20
#define MAXCOL 60
typedef State Grid[MAXROW+2] [MAXCOL+2]
typedef enum state {DEAD,ALIVE} State

Cでtypedefおよびtypedef enumを使用するにはどうすればよいですか?コードのこの部分は何をしますか?

47
user39555
typedef enum state {DEAD,ALIVE} State;
|     | |                     | |   |^ terminating semicolon, required! 
|     | |   type specifier    | |   |
|     | |                     | ^^^^^  declarator (simple name)
|     | |                     |    
|     | ^^^^^^^^^^^^^^^^^^^^^^^  
|     |
^^^^^^^-- storage class specifier (in this case typedef)

typedefキーワードは、疑似ストレージクラス指定子です。構文的には、externstaticなどのストレージクラス指定子が使用されるのと同じ場所で使用されます。ストレージとは何の関係もありません。これは、宣言が名前付きオブジェクトの存在を導入しないことを意味しますが、むしろ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;

変数qpの型はまったく同じです。

typedefのエイリアシングはテキストではありません。たとえば、user_id_ttypedef型の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;

この宣言では、whatyou、およびthinkが「intへのポインター」型であると宣言されていません。マクロ展開が次のようになっているためです。

 int * not, what, you, think;

型指定子はintで、宣言子は*notwhatyou、およびthinkです。したがって、notには期待されるポインター型がありますが、残りの識別子にはありません。

そして、それはおそらく、typedefとCでの型エイリアスについてのすべての99%です。

98
Kaz

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
   }
}
24
Pandrei

補足だけ:

6.7.8型定義

Typedef宣言は新しいタイプを導入せず、指定されたタイプの同義語のみを導入します。

open-std.org ISO/IEC 9899:2017

typedefがCで新しいデータ型を作成することに同意する人々は、free()関数を使用し、名前へのすべての参照をNULLに設定します。

2