このページではC言語における typedef
について解説します。
多くの具体例を踏まえて解説していますので、是非実際の使い方と一緒に typedef
の使い方や効果を理解・実感していただければと思います!
Contents
typedef
とは
typedef
とは既存のデータ型に新たな名前をつけるキーワードです。
例えば既存のデータ型の unsigned int
型 に uint
という新たな名前を付け、その新たな名前の uint
型を用いてプログラミングすることができます。
この時、uint
は unsigned int
と全く同じ型として扱われます。
ポイントは、typedef
では新たな名前を作ることはできるのですが、新たな型自体を作るわけではない点です。
typedef
では元々ある型を違う名前で使用するためのものであって、必ず元となる型が存在します。
typedef
の使い方
typedef
は下記のように2つの型名を指定する形で使用します。
typedef 既存の型名 新たな型名;
1つ目は既存のデータ型名、2つ目は新たに名付けたい型名になります。
例えば int
型を新たに MyInt
と新たに名付ける場合は下記のように記述します。
typedef int MyInt;
既存のデータ型であれば、unsigned int
のように間にスペースがあっても typedef
はきちんと動作してくれます。
typedef unsigned int MyInt;
ただし、下記のような場合はコンパイルエラーになるので注意してください。
;
を最後に付けていない(私は忘れがち)- 1つ目と2つ目の指定が逆
- 1つ目の型名が存在しない
- 2つ目の型名に既に存在している型名を指定している
- 2つ目の型名に使用できない文字を使っている
- おそらく変数名に使用できない文字は使用できない
- スペースもダメ
typedef
で作成した型は、既存の型と同じように使えます。
したがって、新たな型名を用いて下記のようなことを行うことが可能です。
- 変数宣言
- キャスト
- 関数の引数
- 関数の戻り値
sizeof
によるサイズ取得
これらを利用したソースコードの例が下記のようになります。
typedef
で作成した型 MyInt
は、typedef
の1つ目の型に指定した unsigned int
と全く同じサイズ・符号ありなしの型として扱われます。
#include <stdio.h>
#include <stdlib.h>
/* MyInt型を作成 */
typedef unsigned int MyInt;
/* MyInt型を戻り値と引数の型に設定 */
MyInt calc(MyInt, MyInt);
MyInt calc(MyInt a, MyInt b) {
return a + b;
}
int main(void) {
/* MyInt型で変数宣言 */
MyInt a, b, c;
char d = 75;
/* MyInt型でキャスト */
a = (MyInt)d;
b = 1085;
c = calc(a, b);
printf("%u\n", c);
/* MyInt型のサイズを取得 */
printf("%u\n", sizeof(MyInt));
return 0;
}
スポンサーリンク
typedef
の使い方の覚え方
typedef
の使い方に迷った時は、変数宣言を思い出すと良いです。
既存の型名 名前;
これだけだと作られるのは変数ですが、先頭に typedef
を付けてやれば、変数ではなく型が作成されます。
typedef 既存の型名 名前;
つまり、typedef
の使い方は下記のように考えると覚えやすいです。
typedef
の使い方の基本イメージは変数宣言- 変数宣言の前に
typedef
を付けてやれば、変数ではなく型が作られる
例えば下記のように変数宣言を行えば uint
という “変数” が作られることになりますが、
unsigned int uint;
先頭に typedef
をつけることで uint
という “型” が作られることになります。
typedef unsigned int uint;
また、下記のように変数宣言を行えば String
という “変数” が作られることになりますが、
char String[100];
先頭に typedef
をつけることで、String
という “型” が作られることになります。
typedef char String[100];
後に説明する配列の型や関数ポインタの型をtypedef
するときの指定方法はちょっと特殊に見えますが、この覚え方だとすんなり覚えられるのではないかと思います。
typedef
の使用例
ではこの typedef
をどんな時に使用するのか、実際の使用例を踏まえて紹介していきたいと思います。
構造体の型を typedef
する
構造体の型名には必ず struct
が付きますが、typedef
することで構造体の型を struct
なしで利用することができるようになります。
構造体の説明と一緒に typedef
が紹介されるケースも多く、おそらく一番利用されている使い方だと思います。
構造体の型を typedef
するソースコード例は下記のようになります。
#include <string.h>
struct person {
char name[100];
unsigned int age;
double height;
double weight;
};
/* 構造体の型をtypedef */
typedef struct person PERSON;
int main(void) {
PERSON a;
strcpy(a.name, "TARO");
a.age = 15;
a.height = 175.4;
a.weight = 56.3;
return 0;
}
構造体の型を typedef
する例は下記ページでも紹介していますのでこちらも是非参考にしてください。
スポンサーリンク
ポインタの型を typedef
する
ポインタの型を typedef
することも可能です。
typedef
する際、*
の位置は下記の4パターンのどれでも良く、全て同じ結果となります。
typedef char *CharPtr; /* 2つ目の型名の前に*を付ける*/
typedef char* CharPtr; /* 1つ目の型名の後ろに*を付ける*/
typedef char * CharPtr; /* 2つの型名の中間に*を付ける(スペースあり) */
typedef char*CharPtr; /* 2つの型名の中間に*を付ける(スペースなし) */
ポインタの変数宣言やキャスト時等も *
なしに行うことができるようになります。
ただし、型名が変わるだけで変数自体の使い方は通常のポインタと同じであることに注意しましょう。
#include <stdio.h>
#include <string.h>
/* ポインタの型をtypedef*/
typedef char * CharPtr;
int main(void) {
int i;
char a = 'k';
char b[10] = "aiueo\n";
CharPtr x, y;
x = &a;
y = b;
printf("%c\n", *x);
for (i = 0; i < strlen(b); i++) {
printf("%c", *y);
y++;
}
return 0;
}
配列を typedef
する
配列を typedef
することも可能です。
この時、配列のサイズは1つ目の型ではなく、2つ目の型の方で指定します。
typedef char String[100];
直感的には逆な感じがしますがこれが正しいです。
例えば下記のように typedef
することで、char
型のサイズ 100
の配列を String
型として扱うことができます。
String
型の変数は全て、char
型のサイズ 100
の配列となります。
#include <stdio.h>
#include <string.h>
/* 配列をtypedef */
typedef char String[100];
int main(void) {
String str;
strcpy(str, "aiueokakikukeko\n");
printf("%s\n", str);
return 0;
}
関数ポインタの型を typedef
する
C言語では関数ポインタ変数を利用することが可能です。
戻り値の型名 (*変数名)(引数の型名);
戻り値の型名や引数の型名は、ポインタで指したい関数に合わせて記述します(引数の型名は複数指定可能です)。
関数ポインタについては下記ページで解説していますので、詳しく知りたい方はこちらも是非読んでみてください。
C言語の関数ポインタについて解説関数ポインタ変数の宣言は上述のとおり非常に特殊です。私も書くたびにどう書けば良いか悩みます…。
ですが、typedef
を用いることで通常の型同様に下記の形式で変数宣言できるようになります。
型名 関数ポインタ変数;
例えば下記のような関数への関数ポインタについて考えます。
int func(long, char);
この関数を指す関数ポインタ変数を単純に宣言すると下記のようになります(より正確にはこの関数と戻り値と引数の型や個数が同じ関数を指すことができます)。
int (*cb_func)(long, char);
この時 cb_func
が変数名となり、この変数に関数のアドレスを格納することができます。
一方、次のように typedef
すれば、上記関数を指すことのできる FUNC_PTR
型が作成されます。
typedef int (*FUNC_PTR)(long, char);
この FUNC_PTR
を用いて、下記のように関数ポインタ変数を宣言することができます。
FUNC_PTR cb_func1, cb_func_2;
こんな感じで一度 typedef
さえしてやれば、後は簡単に関数ポインタ変数を宣言できるようになります。
下記は関数ポインタの型を typedef
するソースコードの例になります。
#include <stdio.h>
/* 関数ポインタ型をtypedef */
typedef int (*CB_FUNC)(int, char*);
int printName(int, char*);
int printName(int id, char *name) {
printf("%d:%s\n", id, name);
return id + 1;
}
int main(void) {
int ret;
int (*cbFunc1)(int, char*);
CB_FUNC cbFunc2;
cbFunc1 = printName;
ret = cbFunc1(5, "taro");
printf("ret = %d\n", ret);
cbFunc2 = printName;
ret = cbFunc2(10, "hanako");
printf("ret = %d\n", ret);
return 0;
}
int (*cbFunc1)(int, char*);
だと変数宣言している感じがあまりしないですが、
CB_FUNC cbFunc2;
だと直感的に変数宣言していることが分かりやすくなります。
スポンサーリンク
typedef
で型の用途を明示する
C言語の基本的な型は「データのサイズ」と「符号のありなし」を示すだけのものばかりです。
なので、どの型も汎用性は高いというメリットがあります。が、反面どのような用途の型であるかが分かりにくいというデメリットもあります。
型の用途が分かりやすいように typedef
を使って型名を付けてやれば、その型の変数がどのような用途の変数であるかが分かりやすくなります。
例えば色を表すカラーコードは6桁の16進数で表されますので int
型の変数で扱うことが可能です。
普通に int
型を使っても良いのですが、例えば int
を COLOR
型で typedef
してやればより型の用途が明確になります。
ソースコードを読む時でも、型名から「あ、多分色を扱う型だな」とすぐに用途が推測できるので、その分ソースコードが読みやすくなります。
例えば下記の例を見れば COLOR
型を戻り値の型にしている getRed
の方が、より色の情報を返却する関数であることが分かりやすいと思います。
typedef int COLOR;
COLOR getRed(void) {
return 0xFF0000;
}
int getGreen(void) {
return 0x00FF00;
}
typedef
のメリット
最後に typedef
のメリットとデメリットについて紹介しておきたいと思います。まずはメリットから!
ソースコードが読みやすくなる
typedef
で用途の分かりやすい型を作ることでソースコードを読みやすくすることができます。
typedef で型の用途を明示するで紹介したように、型名を工夫するだけでソースコードが読みやすくなります。
これは型名によって変数や関数の戻り値の用途が明確になるからです。
もちろん変数名や関数名を工夫すれば、それだけでソースコード は読みやすくなりますが、型名を工夫すればさらにソースコードが読みやすくなります。
スポンサーリンク
型を楽に記述できるようになる
typedef
で新たな型名を短いものにすれば、その分型を楽に記述できるようになります。
例えば構造体の型を typedef するで紹介したように構造体の型を struct
を付けることなく記述することができるようになります。
unsigned int
なんかも型名としては長いですが、これを uint
なんかに typedef
してやれば、短い記述で unsigned int
と同じ型を使用できるようになります。
typedef
のデメリット
次にデメリットについて説明します。
元々の型が分かりにくい
typedef
のデメリットは元々の型が分かりにくいことです。
例えば構造体の型を typedef するで紹介したように、typedef
を利用することで struct
を略すことについて考えてみましょう。
struct
を記述せずに構造体の型が使えるようになるので、確かに楽になります。
ですが、型名に struct
が無いので構造体の型であることが分かりにくくなります。
そのため、単純なデータ型と考えて使用してしまい、これがバグやコンパイルエラーを招くことになりかねません。
またポインタの型を typedef
する場合でも、もともとの型がポインタ型であることを知らずに使用してしまうと、これも同様にバグやコンパイルエラーを招くことになります。
こんな感じで、元々の型がどのようなものであるかが分かりにくいため、かえって使いにくくなる場合があります。
ただしこのデメリットは typedef
で名付ける型名で元々の型がどのような型であるかが分かりやすくすることで解消できると思います。
スポンサーリンク
まとめ
このページではC言語の typedef
について解説しました。
typedef
により元々ある型に他の名前を名付けることができます。
これにより下記のようなメリットがあります。
- ソースコードが読みやすくなる
- 型を楽に記述できるようになる
typedef
の使い方を忘れた時は変数宣言の仕方を思い出しましょう。変数宣言の前に typedef
を付けることで、変数ではなく新たな型を作成することができます!
オススメの参考書(PR)
C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!
まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。
- 参考書によって、解説の仕方は異なる
- 読み手によって、理解しやすい解説の仕方は異なる
ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?
それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。
なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。
特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。
もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!
入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。
https://daeudaeu.com/c_reference_book/