C言語におけるポインタの勘違い

C言語を初めて間もないとき、ポインタの学習でひとつ勘違いをしていたことがあったので、懐かしい気分に浸りながら、思い出しつつ書き記していこうと思います。

環境依存の話も含まれているので、そこに関しては注意してください。

ポインタはどんな型でも同じサイズらしい

これは本当の話です。ポインタはどんな型であっても、構造体であっても、(同じ環境であれば)同じサイズになります。これは、以下のプログラムで確認できます。

#include <stdio.h>
main(){
	int *ip;
	long *lp;
	char *cp;
	double *dp;

	printf("%d,%d,%d,%d",
		sizeof(ip),
		sizeof(lp),
		sizeof(cp),
		sizeof(dp));
}
> 8, 8, 8, 8

32bit環境なら、全て4になるはずです。私の環境は64bitですから、8となります。

32bitと64bitというのは、扱えるメモリ空間が違うわけです。64bitのパソコンにすると、より多くのメモリを積むことが出来ると言うことは、聞いたことがあるでしょう。ポインタと言うのは、そもそもメモリの番地を格納する変数です。表現しなければならないメモリの番地が大きくなれば、それを表現するためにより大きい数値を格納するにあたり、番地を格納する変数を大きくしなければなりません。

余談でした。

勘違い

結局何かというと、私は「全ての型のポインタが同じサイズならば、ある型に対するポインタでどの型のポインタを使っても同等ではないか」というものです。今となっては、アホ過ぎる考え方です。

結果的に言うと間違いです

私の思い込みが間違いであることを示すには色々な手段がありますが、その中の一部を試してみたいと思います。

#include <stdio.h>
main(){
	// 意図的にこの文字数です
	char a[] = {"abcedfghijklmnopqrstuvwx"};
	int *p;

	for(p = (int*)a; (char)*p != '\0'; p++){
		printf("%c", *p);
	}
}
> adimqu

なかなか面白い結果が得られているのが読み取れます。そこで、以下のようなプログラムを書いてみました。

#include <stdio.h>

main(){
	int i;
	char c;

	printf("%d, %d", sizeof(i), sizeof(c));
}
> 4, 1

上記の結果は環境依存ですが、大抵の環境ではこの値が出てくると思います。これが何を表しているか。つまり、(かなり大雑把に言うと)charはintに比べて1/4ほどの大きさしか持たないということです。

ここで、ポインタのインクリメント(+1する)について考えて見ます。int*とchar*それぞれのポインタに同様のインクリメントをしても、指す場所を動く量が全く異なるということです。int*にインクリメントすれば、単純に言うとchar4つ分ほど指す場所が動いていることになります。配列のポインタをインクリメントすると、常に(それぞれの要素のサイズが異なる場合も)次の要素を指すことを考えます。

メモリの状況

メモリの状況

イメージとしてはこんな感じでしょうか。同じインクリメントでも、メモリの番地的に考えれば、移動量は全く違いますね。

長くなりましたが、このことを踏まえて、前のプログラムを眺めてみます。int*をインクリメントすると、4移動量となります。char配列の4移動量というと、4つ先の要素を指すことが分かります。となると、4の倍数目の要素(文字)が出力されていることの納得がいきます。

逆転の発想

逆転の発想を用いれば、こんなことも出来ます。

#include <stdio.h>

main(){
	int a[] = {1, 2, 3, 4, 5, 0};
	char *p;

	for(p = (char*)a; (int)*p != 0; p += 4){
		printf("%d, ", *p);
	}
}
> 1, 2, 3, 4, 5,

Cのポインタは奥深いですね。

コメントを残す

メールアドレスが公開されることはありません。