突然ですが、次のプログラムを実行してみてください。警告が出るかも しれませんが、問題ないので気にせず実行してください。

#include <stdio.h>

int main(void)
{
	char *cp;
	char str[10];
	char *strp = str;
	int *ip;
	int data[10];
	int *datap = data;

	/* 全て同じ値になるはず */
	printf("sizeof cp = %d\n", sizeof cp);
	printf("sizeof strp = %d\n", sizeof strp);
	printf("sizeof ip = %d\n", sizeof ip);
	printf("sizeof datap = %d\n", sizeof datap);

	return 0;
}

 さて、上のプログラムでは、char型とint型のポインタ変数、そして、 配列のアドレスを持つポインタ変数のそれぞれのサイズを調べて表示させて います。

 まず、配列のアドレスをポインタに代入する時に、&を使っていないことに おかしいと思うかもしれません。実は、配列名だけの場合、その値はアドレス なのです。配列は、『配列名[要素番号]』というふうに、要素番号まで書いて 初めて変数として扱え、配列名のみだとアドレスになるのです。ですから、変数 の時のように&を書く必要がありません。配列名のみというのは、『&配列名[0]』 と同じことなのです。

 次に、各ポインタのサイズを見て、おかしい事に気づくでしょう。 それぞれ型が違ったり、変数と配列の違いがあるのに、全て同じサイズ (おそらく4)になっていると思います。実は、ポインタは型名に関係なく 同じサイズになります。アドレスの大きさというのは、型名に関係なく 同じなので、それを保存するポインタのサイズも全て同じになります。

 では、どうして型名を書くのでしょうか。 『変数を使う』では、型はデータに必要な 大きさがそれぞれ違うから必要、と説明したのだから、この場合、型が必要無い ようにも思えます。なぜ型が必要かというと、ポインタによる『アドレス演算』 ということをするのに必要だからです。

 『アドレス演算』が何か、というと、演算とついていることから分かるかと 思いますが、アドレスに対して、足し算引き算を行う事を言います。例えば、 次のようなことが出来ます。

	int *p;
	p = p + 1; /* p++;でもよい */
	p--; /* p = p - 1;でもよい */
	p = p + 2; /* 1でなくてもよい */

 残念ながら、掛け算割り算は出来ません。とにかく、ポインタに保存されている アドレスに対して、足し算引き算を行う事を『アドレス演算』と言います。この 『アドレス演算』によって何が行われるかというと、アドレスが増えたり減ったり します。アドレスが増えたり減ったりすると、当然そのアドレスから分かる変数は 違う変数になります。

 なぜ、『アドレス演算』が必要なのか、というと、配列のアドレスをポインタ で渡す場合、『&配列名[0]』と0番目の要素のアドレスは渡せますが、その後の 要素のアドレスは渡せません。しかし、配列は必ずアドレスが連続するように 確保されるので、アドレスを増やしたり減らしたりすれば、各要素を使用する事が 出来ます。ですから、『アドレス演算』が出来る必要があるのです。

 しかし、まだ型が必要な理由にはなっていないような気がします。実は、 型が違うと、『アドレス演算』の結果が違ってきます。どう違うかは、次の プログラムを実行してみると分かります。

#include <stdio.h>

int main(void)
{
	char str[10];
	int data[10];
	char *cp = str;
	int *ip = data;

	printf("cp = %p\n", cp);
	printf("cp + 1 = %p\n", cp + 1); /* アドレス差は1のはず */
	printf("ip = %p\n", ip);
	printf("ip + 1 = %p\n", ip + 1); /* アドレス差は4のはず */

	return 0;
}

 それぞれ、1を足したアドレスを見ると、char型のアドレスでは、1増えて いますが、int型では4増えていると思います。ただし、16進数で表現されている ので、もし分からない場合は、Windowsに付いている『電卓』や、その他の関数電卓 というものを使って、16進数を10進数に変換して、その差を計算してみてください。 コラムの『n進数』で一応の説明はしてい ますが、分かり易いかは分かりません。

 コメントにも書いてあるように、ポインタに対して同じ『アドレス演算』を行って いるのに、ポインタの型によってアドレスの増え方が違います。これが、ポインタに 型が必要になる理由です。

 ポインタに対して『アドレス演算』を行うと、アドレスが増えたり減ったりします。 すると、違う変数を示すようになります。この『違う変数』というのがポイントです。 例えば、int型の変数はサイズとして4バイト分のメモリを必要とします。もし、 int型のポインタに対して『アドレス演算』を行い、1バイトだけ増やしたとすると、 3バイトは元の変数のままで、1バイトだけ違う変数を示しています。これでは、 正しくデータが扱えません。int型のポインタに対して『アドレス演算』を行い、 完全に違う変数を示すようにするには、4バイト分変化させる必要があります。

 それに対して、char型ならば、1バイトで済みます。このように、型によって、 『違う変数』にするのに、増やしたり減らしたりする必要があるバイト数が違います。 しっかりと『違う変数』になっていないと、正しくデータが扱えません。ですから、 ポインタにはアドレスを変化させるとき、どれだけ変化させる必要があるのか、 ということが分かるように、型を決めておく必要があるのです。

 まとめです。