ポインタのお話〜ポインタってどういうもの?〜

【ポインタってどういうもの?】

お待たせしました。
いよいよポインタというものがどういうものかを説明します。

ポインタというのは、「指し棒」のことです。
プログラム上で「指し棒」を使いたいときには、このポインタという機能を利用します。

…ん、よくわからない?(^^;)

例えば…
世界地図を広げて見てみましょう。地球儀でもかまいません。
次にあなたの指で、世界地図の東経135度 北緯37度を指差してみてください…。

さて、この状況をプログラム上であると仮定すると、

  • 地図(地球儀) … メモリ
  • 東経&北緯 … アドレス
  • あなたの指 … ポインタ

という関係に当てはめることができます。

ポインタはプログラムで使っている「今見たい情報」に注目するための機能なんです。

ポインタを実際にプログラムで使うには、ポインタ変数として宣言してやる必要があります。
例えば、このように宣言します。

  char *pcData;

使う時には、

  • 今さしているのがどこかを見たい・代入したい … pcData
  • 今さしているところの値を見たい・代入したい … *pcData

という形にします。
実際にどんな値が入っているのかは、以下のようなプログラムを書いて試してみて下さい。

  char *pcData;
  char strData=10;
  pcData = &strData;
  printf("strData[Data:%x,%d]\r\n", strData, strData);
  printf(" pcData[Pointer:%x, Data:%x,%d]\r\n", pcData, *pcData, *pcData);

ここで、&strDataと言う表現が出てきました。
この表現で「strDataという器の番号(アドレス)」を知る事ができます。

  pcData = &strData;

というふうにポインタ変数にアドレス値を渡してやると、ポインタ変数を使ってそのアドレス値が示す器を見に行く事ができます。

言葉ばかりでわかりにくいので、地図(地球儀)の例にもう一度当てはめてみましょう。

  • &strData : 東経135度 北緯37度
  • strData : 東経135度 北緯37度が示している土地の名前
  • pcData : あなたの指
  • *pcData : あなたの指が示している土地の名前

と当てはめる事ができます。ここで、

 pcData = &strData;

とすると、あなたの指をこの位置に持ってきている事になりますので、

  • pcData : あなたの指がさしているのは東経135度 北緯37度
  • *pcData : あなたの指がさしている東経135度 北緯37度にある土地の名前

となります。

ここで、pcData += 北緯1度分 とするとどうなるでしょう?

  • pcData : あなたの指がさしているのは東経135度 北緯38度
  • *pcData : あなたの指がさしている東経135度 北緯38度の土地の名前

となります。
指の位置は、あなたの思うどおりに変える事ができるのですね。

さて、

  char *pcData;

で、宣言した変数でどんな大きさの器が用意されるのかというと、

 ┃│││┃
 ┗┷┷┷┛

これぐらいの大きさです。

ちなみに
short *psData; でも
long *plData; でも

 ┃│││┃
 ┗┷┷┷┛

この大きさなんです。

あれ?とおもいました?
longは同じ大きさなんですが、charと宣言しているのなら

 ┃┃
 ┗┛

これぐらいの大きさだしshortなら

 ┃│┃
 ┗┷┛

ですね。

このポインタ変数の器にはどんな値が入るのかというと、普通の値ではなく、
「アドレス」の値なのです。
アドレスの値を入れるには

 ┃│││┃
 ┗┷┷┷┛

これぐらいの大きさの器が必要なわけなのです。

ここで、全部がこの大きさになるのなら、わざわざ char とか short とかって指定してやらなくってもいいんじゃないの?という疑問が出てくるかもしれません。

用意する器の大きさは同じなのですが、ちょっとだけ動作が違ったりするために、char や short などのデータ型で宣言してやらなければならないのです。

どのように違うのかを、例を出してみましょう。

  char *pcData;
  short *psData;
  long *plData;
  char hsData[10]

という3つのポインタ変数と1つのchar型配列を用意して、それぞれに同じアドレス値が入るように

  pcData = &hsData[10];
  psData = (short*)(&hsData[0]);
  plData = (long*)(&hsData[0]);

とセットしてみましょう。
このとき、それぞれのポインタ変数が見ているアドレス値は下向き矢印(↓)の
ある地点になります。
pcData

  ↓
 ┃│││││││┃
 ┗┷┷┷┷┷┷┷┛

psData

  ↓
 ┃│││││││┃
 ┗┷┷┷┷┷┷┷┛

plData

  ↓
 ┃│││││││┃
 ┗┷┷┷┷┷┷┷┛

さて、ここで、

  pcData++;
  psData++;
  plData++;

とそれぞれのポインタ変数をインクリメントしてみたらどうなるでしょうか?
pcData

    ↓
 ┃│││││││┃
 ┗┷┷┷┷┷┷┷┛

psData

      ↓
 ┃│││││││┃
 ┗┷┷┷┷┷┷┷┛

plData

          ↓
 ┃│││││││┃
 ┗┷┷┷┷┷┷┷┛

見ている位置がこれだけ変わりました。
それぞれデータ型で宣言したとおりの大きさ分だけポインタの位置が進んでいることが分かります。
ポインタ変数は、扱っているデータ型のサイズを覚えてくれているのです。

注意:ポインタ変数はあくまで変数という器を指差すものです。
それ以外のところを指差さないようにしましょう。
プログラムが落ちる原因になります。
例)

  plData = (long*)(&hsData[0]);
  plData+=3;

plData

                  ↓
 ┃│││││││┃ここは
 ┗┷┷┷┷┷┷┷┛変数じゃないよぉ!

ちなみに…
配列の先頭要素のアドレスは

  &hsData[0]

と表現しましたが、

  hsData

としても同じ事になります。

  char hsData[10];
  printf("Address: &hsData[0] = %x, hsData = %x\r\n", &hsData[0], hsData);

と試してみましょう。