Typedefの考え方

# はじめに

関数ポインタを勉強していたら、`typedef` でハマってしまったというお話。間違ってたら、優しく教えてほしいな…（小声）

# まずは基本から

`typedef` といえば、こういう風に書くのが基本だと思います。

```c
// 符号なし整数型を Typedef した場合
typedef unsigned int UINT;
UINT a = 10; 
```

`typedef` したことで、符号なし整数型（`unsigned int`）と同じ意味を持つ新しい型名 `UINT` が出来ました。[^1]

ここから、下記のように公式を導くことができます（私の発見ではなくて、よく見かけるものだけど）。

> typedef 既存の型名 新規の型名

簡単ですね！

## ポインタ

`typedef` といえば構造体でしょうが、ここではポインタを `typedef` しておきたいと思います。ちなみに、下記は K&R にも載っている例です。[^2]

[^2]: こんな例を載せるぐらいなら文字列型を用意しておいてくれよ、と思わなくもないが（笑）

先程の公式に従うと、このようになるでしょう。

> typedef 既存の型名 新規の型名
>
> -->
> 既存の型名: char *
> 新規の型名: STRING
>
> -->
> typedef char* STRING

では、実装してみます。

```c
// char 型へのポインタ（文字列）を typedef した場合
typedef char* STRING;
STRING str = "ABCDEFG";
```

見事、文字列型が出来ました！

## 関数ポインタ

ところで、関数もメモリに配置されているため、ポインタで指すことができます。あまり面白くないコードですが、こんな感じです。

```c
#include <stdio.h>

// 関数ポインタを通して呼び出される関数
void func(int a); 

int main() {
    // 関数ポインタを宣言する
    void (*funcPtr)();

    // 関数ポインタに関数名（関数のアドレス）を代入する
    funcPtr = func;

    // 関数ポインタを間接参照し、関数を呼び出す
    (*funcPtr)(1);

    return 0;
}

void func(int a) {
    printf("Input value is %d\n", a);   
}
```

関数ポインタで一番わかりにくいのは、下記の部分だと思います。

```c
void (*funcPtr)();
```

これは「『`void` 型を戻り値とする関数』へのポインタ」を宣言しています。

```c
void *funcPtr();
```

こんな風に書きたくもなりますが、こう書くと、「`void` 型へのポインタを戻り値とする関数」を宣言したことになってしまいます。それを防ぐために、`()` を付けることになったそうな…[^3]

[^3]: 演算子の優先順位が原因なのですが、あれを覚える気力が湧かない…

ちなみに、「配列名」が「配列の先頭要素のアドレス」を指しているように、「関数名」も「関数のアドレス」を指しています。なので、ポインタにアドレスを代入する際も `&func` とする必要はありません。

```c
funcPtr = func;
```

# 関数ポインタをtypedefしたい！

さて、ポインタを `typedef` できたなら、関数ポインタもできるはずです。実際、関数ポインタは長くなりがちなので、`typedef` することも多いらしいです。

まずは公式に当てはめて考えてみます。

> typedef 既存の型名 新規の型名
>
> -->
> 既存の型名: void (*)()
> 新規の型名: FUNCTYPE
>
> -->
> typedef void (*)() FUNCTYPE

うん、なんか不格好だけど、やってみよう！

```c
#include <stdio.h>

void func(int a); 

int main() {
    typedef void (*)() FUNCTYPE;

    FUNCTYPE funcPtr = func;
    funcPtr(1);

    return 0;
}

void func(int a) {
    printf("Input value is %d\n", a);   
}
```

あれっ、エラーが出てコンパイルできないぞ…

## 正しい書き方はこれ

いろいろと調べると、以下の書き方が正しいみたい。

```c
typedef void (*FUNCTYPE)();
```

## 公式への疑惑

うーん、たしかに動いたけど、これって、公式と違わない？

> typedef 既存の型名 新規の型名

そう思って調べてると、こんな [文章](https://groups.google.com/d/msg/fj.comp.lang.c/payvwUb0G0E/XU__C3vy_vAJ) を見つけました。

> 私も typedef の話をする時には、「まず変数宣言をするふりをして、頭に typedef を付けると型になる」と説明しています。

えっ、そういうこと？

## typedefと変数宣言

変数宣言を念頭に考えると、`typedef` は一気にわかりやすくなります。例えば、符号なし整数型の場合。

```c
// 0.変数を宣言する
unsigned int a;

// 1.typedef を先頭につける
typedef unsigned int a;

// 2.変数名を新しい型名にする => 完成！
typedef unsigned int UINT;
```

文字列型を作る場合も同じ。

```c
// 0.変数を宣言する
char* str;

// 1.typedef を先頭につける
typedef char* str;

// 2.変数名を新しい型名にする => 完成！
typedef char* STRING;
```

そして、関数ポインタの場合。

```c
// 0.変数を宣言する
void (*funcPtr)();

// 1.typedef を先頭につける
typedef void (*funcPtr)();

// 2.変数名を新しい型名にする => 完成！
typedef void (*FUNCTYPE)();
```

実は配列も `typedef` できるのですが、これも最初に考えた公式では到底導き出せません。

```c
// 0.変数を宣言する
int numbers[5];

// 1.typedef を先頭につける
typedef int numbers[5];

// 2.変数名を新しい型名にする => 完成！
typedef int ARRAY[5];

// 実際の使い方
ARRAY hoge = {1, 2, 3, 4, 5}; 
```

## 規格ではどうなってるの？

コンパイラがこういう風に解釈しているのだから、規格にも記述があるはず。ということで、K&R を探してみました。以下の場所がそれっぽい？[^4]

[^4]: 『プログラミング言語C』第2版、p.276

> 宣言子中の各名前には、一つの `typedef` 宣言により、通常のやり方で型が与えられる（8.6節）。その後では、そうした `typedef` 名は、その型に対応する型指定子キーワードと文法的には同じになる。

ここでいう「8.6節」には、ポインタや配列、関数などの宣言方法が書かれています。それと同じようにして型が与えられるということは、変数宣言と同じようにして `typedef` もできるということでしょうか？

# まとめ

よく聞く公式？

> typedef 既存の型名 新規の型名

では汎用性が低いので、下記の手順を踏むようにしましょう。

>0.変数を宣言する
>1.typedef を先頭につける
>2.変数名を新しい型名にする

[^1]: 新しい **型** が出来たわけではなく、**型名** が出来ただけであることに注意。