C言語のtypedefについて具体例を用いて分かりやすく解説

typedef解説ページのアイキャッチ

このページではC言語における typedef について解説します。

多くの具体例を踏まえて解説していますので、是非実際の使い方と一緒に typedef の使い方や効果を理解・実感していただければと思います!

typedef とは

typedef とは既存のデータ型に新たな名前をつけるキーワードです。

例えば既存のデータ型の unsigned int 型 に uint という新たな名前を付け、その新たな名前の uint 型を用いてプログラミングすることができます。

この時、uintunsigned int と全く同じ型として扱われます。

ポイントは、typedef では新たな名前を作ることはできるのですが、新たな型自体を作るわけではない点です。

typedef では元々ある型を違う名前で使用するためのものであって、必ず元となる型が存在します。

typedef の使い方

typedef は下記のように2つの型名を指定する形で使用します。

typedefの使い方

typedef 既存の型名 新たな型名;

1つ目は既存のデータ型名、2つ目は新たに名付けたい型名になります。

例えば int 型を新たに MyInt と新たに名付ける場合は下記のように記述します。

intにMyIntを名付ける例

typedef int MyInt;

既存のデータ型であれば、unsigned int のように間にスペースがあっても typedef はきちんと動作してくれます。

unsigned intにMyIntを名付ける例

typedef unsigned int MyInt;

ただし、下記のような場合はコンパイルエラーになるので注意してください。

  • ; を最後に付けていない(私は忘れがち)
  • 1つ目と2つ目の指定が逆
  • 1つ目の型名が存在しない
  • 2つ目の型名に既に存在している型名を指定している
  • 2つ目の型名に使用できない文字を使っている
    • おそらく変数名に使用できない文字は使用できない
    • スペースもダメ

typedef で作成した型は、既存の型と同じように使えます。

したがって、新たな型名を用いて下記のようなことを行うことが可能です。

  • 変数宣言
  • キャスト
  • 関数の引数
  • 関数の戻り値
  • sizeof によるサイズ取得

これらを利用したソースコードの例が下記のようになります。

typedef で作成した型 MyInt は、typedef の1つ目の型に指定した unsigned int と全く同じサイズ・符号ありなしの型として扱われます。

MyIntを利用したソースコード例

#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 という “変数” が作られることになりますが、

uint変数の宣言

unsigned int uint;

先頭に typedef をつけることで uint という “型” が作られることになります。

uint型の宣言

typedef unsigned int uint;

また、下記のように変数宣言を行えば String という “変数” が作られることになりますが、

String変数の宣言

char String[100];

先頭に typedef をつけることで、String という “型” が作られることになります。

String型の宣言

typedef char String[100];

後に説明する配列の型や関数ポインタの型をtypedef するときの指定方法はちょっと特殊に見えますが、この覚え方だとすんなり覚えられるのではないかと思います。

typedef の使用例

ではこの typedef をどんな時に使用するのか、実際の使用例を踏まえて紹介していきたいと思います。

構造体の型を typedef する

構造体の型名には必ず struct が付きますが、typedef することで構造体の型を struct なしで利用することができるようになります。

構造体の説明と一緒に typedef が紹介されるケースも多く、おそらく一番利用されている使い方だと思います。

構造体の型を 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 する例は下記ページでも紹介していますのでこちらも是非参考にしてください。

構造体解説ページのアイキャッチ【C言語】構造体について初心者向けに分かりやすく解説

スポンサーリンク

ポインタの型を typedef する

ポインタの型を typedef することも可能です。

typedef する際、* の位置は下記の4パターンのどれでも良く、全て同じ結果となります。

ポインタの型をtypedefする複数のパターン

typedef char *CharPtr; /* 2つ目の型名の前に*を付ける*/
typedef char* CharPtr; /* 1つ目の型名の後ろに*を付ける*/
typedef char * CharPtr; /* 2つの型名の中間に*を付ける(スペースあり) */
typedef char*CharPtr; /* 2つの型名の中間に*を付ける(スペースなし) */ 

ポインタの変数宣言やキャスト時等も * なしに行うことができるようになります。

ただし、型名が変わるだけで変数自体の使い方は通常のポインタと同じであることに注意しましょう。

ポインタの型をtypedefする例

#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

typedef char String[100];

直感的には逆な感じがしますがこれが正しいです。

例えば下記のように typedef することで、char 型のサイズ 100 の配列を String 型として扱うことができます。

String 型の変数は全て、char 型のサイズ 100 の配列となります。

配列をtypedefする例

#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する例

typedef int (*FUNC_PTR)(long, char);

この FUNC_PTR を用いて、下記のように関数ポインタ変数を宣言することができます。

関数ポインタ型をtypedefした時の変数宣言

FUNC_PTR cb_func1, cb_func_2;

こんな感じで一度 typedef さえしてやれば、後は簡単に関数ポインタ変数を宣言できるようになります。

下記は関数ポインタの型を 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 型を使っても良いのですが、例えば intCOLOR 型で typedef してやればより型の用途が明確になります。

ソースコードを読む時でも、型名から「あ、多分色を扱う型だな」とすぐに用途が推測できるので、その分ソースコードが読みやすくなります。

例えば下記の例を見れば COLOR 型を戻り値の型にしている getRed の方が、より色の情報を返却する関数であることが分かりやすいと思います。

COLOR型で用途を明確にする例

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 を付けることで、変数ではなく新たな型を作成することができます!

オススメの参考書

C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!

まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。

  • 参考書によって、解説の仕方は異なる
  • 読み手によって、理解しやすい解説の仕方は異なる

ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?

それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。

なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。

特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。

もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!

入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。

https://daeudaeu.com/c_reference_book/

同じカテゴリのページ一覧を表示