C言語でのtypedefと#defineの違いについて書きます ^^;)
「 typedef, #define共に、変数型を別の任意の文字列で宣言できる。双方の挙動にさほど大きな違いはない 」
・・・と誤認されがちなのですが、実際の挙動は全く異なります。
まずは、以下のコードを見てください。
#include <stdio.h>
#define char_ptr char *
typedef char * char_ptr2;
int main(void)
{
char_ptr a1, a2, a3;
char_ptr2 b1, b2, b3;
printf("%d\n", sizeof(a1));
printf("%d\n", sizeof(a2));
printf("%d\n", sizeof(a3));
printf("%d\n", sizeof(b1));
printf("%d\n", sizeof(b2));
printf("%d\n", sizeof(b3));
return 0;
}
結果)
32bitCPU用のポインタのサイズは 4byte(=32bit)なので、全て4が表示されるはずじゃね?!Σ(゚Д゚)
と驚いた人のみ、以下を読み進めて下さい。
(↑の結果が「至極当たり前じゃん」と理解している人は、もうこれ以上読み進める必要はありません ^^)
さてさて、実は #defineとtypedefは、それぞれを解釈するプログラムが異なります。
#defineはプリプロセッサにより解釈され、typedefはコンパイラにより解釈されます。
周知の通りプリプロセッサとは、コンパイラがソースコードを解釈する前に、ソースコードへ文字列レベルの処理を加える プログラムです。
(だからこそプリプロセッサの事を、「前処理系」なんて表現を使ったりもします。)
ファイルを読み込む #includeやら、コードを切り分ける #ifdef ... #endif、#defineによるマクロが有名ですね ^^
では、以下のコードをプリプロセッサが解釈したら、どんなソースコードが生成されるのでしょうか??
#define char_ptr char *
char_ptr a1, a2, a3;
答えは次の通りです。
char * a1, a2, a3;
この場合、a1のみchar型のポインタとなり、a2, a3はchar型の変数となります。(詳細は、C言語の入門書を読んでください^^)
その為、冒頭のコードの様な結果が表示された訳です。
一方
typedefの場合は、単純なソースコード文字列の置換ではなく、コンパイラが型情報を保持
しています。
(言うならば「変数型に別名でアクセスしている」状態です。)
その為、デバッガで変数を追いかける際にも、typedefの定義は有効です。
(※ #defineを使った定義では、全く考えられない話)
typedefの使い道としては、
例えば関数へchar型ポインタの配列を渡す場合等に、
typedef char * char_ptr;
void func(char_ptr * aaa) {...
とすると、2重ポインタを渡すよりも、幾分コーディングが楽になったり、
構造体ポインタを多用する際などは、
struct _tagHOGE * aaa と書くよりも、間違いなく
typedef struct _tagHOGE {
...
} hoge;
hoge * aaa;
と書いた方が、間違いなくコードの可読性があがります。
そして何より、(上でも書きましたが)デバッグ時に型情報が維持される事が、とても有難いです。