C言語での複雑な型表記を理解する方法

ブックマークに書いたけど、自分の認識を明確にするため、少し詳しく書いておくことにしてみた。

C言語で、

char *(*(**foo [][8])())[]

みたいな型はどういう意味を持つかは、ぱっと見わかりにくいという話題。

自分にとっての理解するための手順は以下のとおり:

  • 0: その型をtypedefする
  • 1: typedefで分解する
    • 外側の()に注目し、()の内側(= PART)を切り出し、仮の型名NAMEを入れる
    • 下に「typedef NAME PART;」を置く
    • これを()がなくなるまで繰り返す
  • 2: 上左から読んでいく(英語だと逆)
    • (ただし、配列の配列は逆から)
//typedef char *(*(**foo [][8])())[];
typedef char *(ret_t)[]; 
//typedef ret_t *(**foo [][8])();
typedef ret_t *(func_t)(); 
typedef func_t **foo[][8]; 

最後のtypedefは、以下のように括弧が付けられ、同様に分解、解釈できる:

//typedef func_t *(*((foo[])[8]));
typedef func_t *t1; 
typedef t1 *t2;
typedef t2 t3[8];
typedef t3 foo[];

よって「foo型は、char*配列/のポインタを返す関数/へのポインタのポインタの8要素配列の配列」

英語と日本語では、話順が逆になるけど、この場合は日本語のほうが有利じゃないかな。

全部型の基本構文で分解すると、

////typedef char *(*(**foo [][8])())[];
//typedef char *(ret_t)[]; 
typedef char *pchar;
typedef pchar ret_t[]; // <=> typedef pchar (ret_t)[];

////typedef ret_t *(**foo [][8])();
//typedef ret_t *(func_t)(); 
typedef ret_t *pret_t;
typedef pret_t func_t(); // <=> typedef pret_t (func_t)();

//typedef func_t **foo[][8]; 
typedef func_t *t1; 
typedef t1 *t2;
typedef t2 t3[8];
typedef t3 foo[];

型構文の基本は、名前や構造/ポインタ/配列/関数で、その形式も変数宣言や関数宣言と同じ形になるようになっている。typedefの場合、識別子にあたるところが、新たな型名になるので、分解してしまえさえすれば、どんなに複雑な表記だろうと素直に理解できると思う。

以下、コード上に書き下したもの、コンパイルしてエラーが出ないことを確認:

// Understanding complex type declarations in C language.

// example: a complex type declaration is:
//
//    char *(*(**foo [][8])())[]
//

// step 0: typedef the decl
// step 1: destruct the type with "typedef"s according to outer-left ()s

//typedef char *(*(**foo [][8])())[];
typedef char *(ret_t)[];
//typedef ret_t *(**foo [][8])();
typedef ret_t *(func_t)();
typedef func_t **foo[][8];

// step 2: read from right in English(or from left in Japanese)
//
// "foo" is array of 8-array of pointer of pointer of
// function returning pointer of
// array of char pointer.

// check test: compile with no warnings and no errors on type checking
typedef char *(*(**foo2 [][8])())[];
int main() {
  foo2* a = 0;
  foo* b = a;
  a = b;

  return 0;
}