C言語のポインタの型の意味は?

ポインタの型の解説ページのアイキャッチ

下のページでポインタは他の変数のアドレスを指す矢印であることを説明しました。ただしこのページではポインタの型については触れていません。

ポインタ解説ページのアイキャッチ徹底図解!C言語ポインタを初心者向けに分かりやすく解説

char* であろうが int* であろうが long* であろうが全て、ポインタがアドレスを指す矢印であることは変わりません。では、この型の違いによって具体的にどのような違いが出てくるのでしょうか?この辺りをこのページで解説したいと思います。

ポインタの型による動作の違い

ポインタの型によってプログラムの動作がどのように変わるかを、実際に下記のプログラムで確認してみましょう。

#include <stdio.h>

int main(void){
  char c;
  int i;
  long l;

  char *cp = &c;
  int *ip = &i;
  long *lp = &l;

  printf("sizeof c = %d\n", sizeof(c));
  printf("sizeof cp = %d\n", sizeof(cp));
  printf("cp = 0x%x\n", cp);
  printf("cp+1 = 0x%x\n", cp+1);
  
  printf("sizeof i = %d\n", sizeof(i));
  printf("sizeof ip = %d\n", sizeof(ip));
  printf("ip = 0x%x\n", ip);
  printf("ip+1 = 0x%x\n", ip+1);
  
  printf("sizeof l = %d\n", sizeof(l));
  printf("sizeof lp = %d\n", sizeof(lp));
  printf("lp = 0x%x\n", lp);
  printf("lp+1 = 0x%x\n", lp+1);

  return 0;
}

ここではchar型とint型とlong型とで、それぞれ下記の値を確認しています。

  • 型のサイズ
  • ポインタのサイズ
  • ポインタのアドレス
  • ポインタを1つプラスした時のアドレス

私のPCで実行すると下記のような結果になりました。

MEMO

私のPCは 64 bit の CPU のため、ポインタ(アドレス)のサイズが8バイトになっています

32 bit CPU の PC を使用している方はポインタのサイズは4バイトのはずです

sizeof c = 1
sizeof cp = 8
cp = 0xe90d3afb
cp+1 = 0xe90d3afc
sizeof i = 4
sizeof ip = 8
ip = 0xe90d3af4
ip+1 = 0xe90d3af8
sizeof l = 8
sizeof lp = 8
lp = 0xe90d3ae8
lp+1 = 0xe90d3af0

ポインタのサイズはポインタの型に関わらず同じサイズになっています。注目して欲しいのは下記のポイントです。

「ポインタのアドレス」と「ポインタを1つプラスした時のアドレス」の差が「型のサイズと同じ」

ポインタの型によって動きが異なるのはここです。ポインタに対して同じ + 1 を行なった場合でも、アドレスの変化量がポインタの型によって違います。ポインタに対して +1 した場合、アドレスは型のサイズ分増加することになります。

具体的には、char 型はサイズが1バイトなので、char* のポインタを +すると、1バイト分アドレスが移動しますが、int 型はサイズが4バイトなので、int* のポインタを +1 すると、4バイト分アドレスが移動します。

char型の場合を図示すると下記のようになります。1つの箱が1バイトを表していると考えてください。

char型のポインタの動き

cp はポインタ型なので8バイトの領域の中にアドレス 0xe90d3afb が格納されており、アドレス 0xe90d3afb を指しています。一方 cp + 1 ではそのアドレスに1バイト分プラスしたアドレス 0xe90d3afc を指しています。また cp + 3 は 0xe90d3afb に1バイト分 x 3プラスしたアドレス 0xe90d3afe を指しています。

続いて int 型を見てみましょう。

int型のポインタの動き

ip はポインタ型なので char の時と同様に8バイトの領域の中にアドレスが格納されています。ip には 0xe90d3af4 が格納されており、アドレス 0xe90d3af4 を指しています。一方 ip + 1 ではそのアドレスに4バイト分プラスしたアドレス 0xe90d3af8 を指しています。

ポインタの型によってアドレス増加量が異なる理由

もし int のポインタでも、+1 しても1バイト分しかアドレスが移動しない場合のことを考えてみましょう。例えば下記のように2つの int 型の領域に値が格納されているとします(値は16進数で記載しています)。

ポインタに型があるメリット

この状態で ip + 1 したアドレスの値を取得すると、2つの値と全く異なる 0xDCBA9876 が取得されてしまい、おそらく想定していないアドレスにアクセスしていることになります。

次の int の領域にアクセスするためにはポインタに int 型のサイズである +4 をわざわざ行う必要があり、毎回変数の型や変数の型のサイズを意識しながらポインタ操作をする必要が出てきて面倒です。また予期しないアドレスにアクセスしてしまうバグも発生しやすくなってしまいます。こういうことを防ぐためにポインタの型によってアドレスの増加値が異なるようになっていると考えられます。

スポンサーリンク

他の型の変数を指した場合の動き

ポインタは変数宣言時に型を宣言しますが、実は宣言時の型以外の変数も指すことが可能です。例えば下記のように char* で宣言したポインタで int 型の変数を指すことできます。

char *ptr;
int x;
ptr = &x;

このように char* で宣言したポインタで、もっと大きいサイズの型(例えば int)の変数を指すことで、その変数にバイト単位でアクセスすることが可能になります。

例えば下記のプログラムでは char* 型のポインタ cp を用いて int 型の変数 i に対して1バイトずつアクセスして値を表示しています。

#include <stdio.h>

int main(void){
  char *cp;
  int *ip;
  int i = 0x7654321;

  ip = &i;
  cp = (char*)ip;
 
  printf("cp = 0x%x\n", *cp);
  printf("cp+1 = 0x%x\n", *(cp+1));
  printf("cp+2 = 0x%x\n", *(cp+2));
  printf("cp+3 = 0x%x\n", *(cp+3));
  printf("ip = 0x%x\n", *ip);

  return 0;
}

実行結果は下記のようになります。

cp = 0x21
cp+1 = 0x43
cp+2 = 0x65
cp+3 = 0x7
ip = 0x7654321

このように、cp を使用することで1バイトずつ int 型の変数の値にアクセスして表示できていることが確認できると思います。

他の型のポインタで指す様子

cp には 0x7654321 の頭側の 0x7 が格納されているのかと思いましたが、0x21 が格納されていたので少し驚きました。これは私のPCのCPUがインテルでリトルエンディアン(値の下位がバイト単位に上位のアドレスから順に格納される)だからのようです。

まとめ

このページではポインタの型の違いについて解説しました。ポインタの型による違いは、ポインタの値に +1 した時に増加するアドレスの大きさです。

これを利用して int 型の変数等に1バイトずつアクセスすることも可能です。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です