typedef

新しい型を作る

typedefは、既存のデータ型に新しい名前を付けるためのキーワードです。


#include <stdio.h>

typedef unsigned int uint;

int main()
{
    unsigned int num1 = 10;
    uint num2 = 20;

    num1 = num2;
}

このコードではtypedefによってunsigned int型は「uint」という名前でも使用できることを定義しています。

「uint」と「unsigned int」は結局同じデータ型なので、見かけは違っても同じ物として使用することができます。
それぞれの変数同士を互いに代入することもできます。

「unsigned int」といちいち記述するのが面倒ならば、このように短い名前に変えてしまうことができます。

他にも、データ型の意味を明確にするためにあえて名前を変える場合もあります。
例えばintやlong型などただの整数型に過ぎませんが、データのサイズを示す意味を明確にするために、C言語ではtypedefを用いて「size_t」というデータ型を定義しています。

Visual C++ではsize_t型はunsigned int型(32bit版)またはunsigned __int64型(64bit版)の別名です。

#defineとの違い

データ型に別の名前を与えるのは、#defineを使っても実現可能です。


#define uintD unsigned int
typedef unsigned int uintT;

int main()
{
    uintD d = 10;
    uintT t = 20;

    d = t;
}

しかし、実はこのような書き方はC言語の文法的には正しくありません。
(Visual Studioではコンパイルが通って実行できますが)

typedefはその名の通り、type(型)をdefinition(定義)するための機能です。
#defineはコードの置き換え機能に過ぎず、思わぬ置き換えが行われてしまうことがあるので、新しい型を作る場合は#defineではなくtypedefを使うようにしましょう。

ポインタ型の別名

#defineとの違いは、ポインタ型に別名を与える場合に顕著になります。

char型のポインタを変数をふたつ宣言したくて、このように書いたとします。


#define str char*

str moji1, moji2;

しかしこれは以下のように解釈されます。


//strをchar*に変換
char* moji1, moji2;

//↓以下と同義
char *moji1;
char moji2;

ポインタ変数をふたつ宣言したつもりが、先頭の変数だけがポインタ変数となり、ふたつ目以降は通常のchar型の変数と解釈されてしまうのです。

新しい型を作る場合はやはりtypedefを使用した方が安全です。


typedef char* str;

//両方ポインタ変数
str moji1, moji2;

配列ごと別名にする

typedefは配列であってもそのまま別名を与えることができます。


typedef unsigned int uintT[10];

//「uintT」と宣言するだけで配列になる
uintT t = { 1, 2, 3 };

//こんなのとか
#define uintD[10] unsigned int
//こんなのはできない
#define uintD unsigned int[10]

//こういうのはできる
#define uintD unsigned int
uintD d[10] = { 1, 2, 3 };

#defineでも、通常のデータ型に別名を与え、変数の宣言時に配列にすることはできます。

構造体とtypedef

typedefは構造体を使用するときによく用いられます。
構造体については次の項で説明します。
構造体