第1章 シノニムの定義

 さぁ、とうとう第3部が始まります。ここでは第2部と平行して、第1,2部では語られないC/C++言語のもっと深いところに触れていきます。

 その第1回目として、今回は typedef について話します。typedef 自体は第1部第60章で出てきましたが、詳しい意味については第3部で話すと言っていました。その約束を、今、果たしましょう。

 ちなみに、「シノニム」とは「同義語」という意味です。ヘルプでは「シノニム」と書かれていることがあるので、知っておいた方がいいでしょう。


 では、今回の要点です。


 では、いってみましょう。


 さて、パス名を入れるバッファを作りたいと思います。パス名とはファイルの置いてある位置の名前のことで、バッファとはデータを置いておく容器(変数)のことです。

 パス用のバッファの最大長は stdlib.h というヘッダファイルの中に、_MAX_PATH というマクロで定義されています。つまり、このバッファは、例えば

char szPath[_MAX_PATH];

のように宣言されるわけです。

 しかし、このパス用のバッファを沢山使いたいときは、この宣言はちょっと面倒ですね。そして、もっとパス用のバッファということが分かりやすいように、こう見ただけでパス用のバッファだなぁ、というようにはできないものでしょうか?

 つまりは、パス用のバッファ用の型なんてのがあったら、便利なのではないでしょうか?

 で、「ないなら作ってしまえ!」ということで、次のようにしてみました。

#define PATHBUF(name)  char name[_MAX_PATH]
PATHBUF(szPath);

 マクロです。確かにできるにはできるのですが、ちょっと格好悪いですよね。こう、普通の型と同じ様な宣言文にできないものでしょうか?

 ということで、こういうときのために typedef というものがあります。typedef を使えば、ある型の同義語を定義することができます。「char 型の _MAX_PATH 個の要素を持った配列」という型の同義語を作れば、望みの動作になるわけです。

 では、typedef はどのようにして使うのでしょうか? それは簡単です。先ず、その型の変数の宣言文を書きます。変数の名前は作りたい同義語(ここでは PATHBUF にします)にして下さい。

char PATHBUF[_MAX_PATH];

 で、次にこの文の先頭に typedef と書きます。

typedef char PATHBUF[_MAX_PATH];

 これで「char 型の _MAX_PATH 個の要素を持った配列」という型の同義語 PATHBUF が定義されました。ここで注意することは、変数の宣言は行っていないということです。型を作っただけなのです。

 「その型の変数の宣言文を書きます」という表現が紛らわしかったかも知れませんが、ここでは変数の宣言は全く行われていません。「文法的に一緒」ということが言いたかっただけなのです。このように、変数の宣言文の頭に typedef を付けると、変数の宣言文ではなく、同義語の定義文になります

 では、早速この型を使ってみましょう。

PATHBUF szPath;

 実にすっきりしましたね。これで、配列宣言でごちゃごちゃしたプログラムも、かなりすっきりとすることでしょう。この変数を使うときも、普通の配列と同じように

szPath[0] = 0;

のようにできます。このように、「同義語」というからには char szPath[_MAX_PATH]; の時と動作は同じなのです。


 ただし、1つだけ注意することがあります。それは、ポインタ型の同義語についてです。

 さて、文字列へのポインタ char* がありますね。これをもっと分かりやすく PSTR という同義語で扱うようにしたいと思います。

typedef char* PSTR

 さて、これを利用して、文字列の長さを取得する関数 strlen を作ってみたいと思います。引数の文字列をうっかり変更してしまってはいけないので、const を付けようと思います。

int strlen(const PSTR str)
{
    int i;
    for(i = 0; *str; str++)
        i++;
    return i;
}

 さて、コンパイルしましょう...と、何やらエラーが出てきましたね。

error C2166: 左辺値は const オブジェクトに指定されています。

 このエラーは、const 定数に指定されている変数などの値を変えようとしたときに出てきます。このエラーはこの行で出ているみたいです。

for(i = 0; *str; str++)

 i は絶対に const 定数ではないので、どうやら str++ をしたのがまずかったらしいです。

 しかし、PSTR の部分を char* にしてみると、このエラーは出ません。どうやら、同義語にしたときだけ問題が起こるようです。

 実は、const PSTR というのは char* const と同じ事になるのです。しかし、この char* const って何だ!?

 忘れている人も多いでしょうが、第1部第42章で言った通り、これはポインタ自身が定数になるという型です。参照先は定数にならず、書き換えることが可能です。

 const int n; で n が定数になるように、const PSTR str; では str が定数になるのです。こう考えればそうなる理由もよく分かりますね。

 では、const char* str と同じようにしたいときはどのようにすればいいのでしょうか? これは面倒ですが、別にこの型を作るより他はありません。

typedef const char* PCSTR;

 この PCSTR を使えば、

int strlen(PCSTR str)
{
    int i;
    for(i = 0; *str; str++)
        i++;
    return i;
}

となります。これで、無事コンパイルできるようになります。

 ここで *str = 0; という一文をどこかに加えてみて下さい。上のと同じエラーが出てくると思います。

 このように、参照先を const とするポインタ型を使いたいときは、そういう型を作らなければならないわけです。欠点といえば欠点なのかも知れませんが、文法上致し方がないという気もします。


 最後に、マクロと typedef との違いです。

 マクロは何度も言うようにテキストの置き換えです。しかし、typedef は純粋に言語の機能として働きます。つまり、テキストの置き換えが起こるわけではありません。typedef は型の定義なのです。

 #define のように先頭に # の付いた命令をプリプロセッサディレクティブと言います。これは第1部第70章にも言ったことです。これらは「コンパイラがソースファイルを解析する前に」処理されると言いました。一方、typedef は「コンパイラがソースファイルを解析する時に」処理されます。

 マクロと typedef とが全く違うものであるということは、上の2例(配列の例、ポインタの例)ではっきりとするでしょう。sizeof や型キャストの動作なども試してみると、もっとよく分かるでしょう。

 マクロと typedef を同一視しているような参考書があれば、その参考書の他の部分も疑った方が良さそうです。両者は根本的に異なるものなのです。


 これで今回は終わりです。第1部第60章の構造体における typedef の働きについては、自分で考えてみて下さい。typedef は構造体の同義語も作れるわけで...と、ヒントはこの辺でやめておきますね。

 では、今回の要点です。


 それでは、次回まで。


第1部第80章 次のステップへ | 第2章 近道

Last update was done on 2000.8.2

この講座の著作権はロベールが保有しています