3 ポインターとなにか

3.1 ポインターの例

これまでの話で、メモリーというものが大体分かったと思う。そして、その内容とともに、 アドレスが重要であることも分かったであろう。あるいは、アドレスを上手に操作すれば、 いろいろなこと(悪いことも)ができそうだと分かったであろう。

アドレスを操作するとなると、アドレスを入れる変数が欲しくなる。 2.3節で述べたように、アドレスは32ビットである。また、 int型のデータも32ビットである。従って、int型の変数にアドレスを入れるこ とがあできそうである。具体的には、hogeと言う変数のアドレスをint型の変 数 i に、次のような文で、

	i=&hoge;
と代入する。しかし、これはコンパイラーにより警告が出され、推奨される方法でない 5。たまたま、 私が使っているコンパイラーでは警告で済んでいるが、エラーを出すものもあるであろう。 そもそも、アドレスのビット数とint型のビット数が同じであるのは偶然にすぎない。

幸いなことに、C言語にはアドレスを格納する仕組みが用意されている。ポインターとい う変数を使い、アドレスが格納できるのである。そのアドレスを格納するポインター型変 数は、

	int *pi;
	double *px;
と宣言する。アスタリスク(*)をつければ、ポインターの宣言になる。

整数型変数 i と実数型変数 x のアドレスは、&i&xのようにすると取り出すことができる。アドレス演算子(&)を使うのであ る。取り出したアドレスは、ポインターに

	pi=&i;
	px=&x;
のようにして代入できる。アドレス演算子(&)により変数の先頭アドレスを取り出 して、代入演算子(=)を用いて、ポインター型変数に代入している。

ポインター機能は、アドレスの格納のみに止まらず、そのアドレスが示しているデータの 内容も表すことができる。今までの例の通り、ポインターには変数の先頭アドレスが格納 されている。そして、ポインターの宣言の型から、そのポインターが指しているデータの 内容までたぐり寄せることができる。ポインターpipxが示しているデータの 値を、整数型変数 j と実数型変数 y に代入する場合

	j=*pi;
	y=*px;
とかく。ここで、アスタリスク(*)は間接参照演算子で、ポインターが示しているア ドレスのデータを取り出せるのである。このようにアドレスのみならず、そのアドレスの データの型までポインターは持っているから、これが可能なのである。このことから、アドレ スとは言わずにポインター(pointer 指し示すもの)と言うのであろう。

3.2 プログラム例

実際のプログラムで見てみよう。リスト2のプログラムは、 の動作は以下の通りである。これにより、ポインターの意味とそれに関わる演算子の動作 の基礎的なことを理解する。

このプログラムの各行の内容は、以下の通りである。1行毎にきっちり理解することが重 要である。

4行
整数型のポインターpを宣言している。pに整数型のデータの先頭 アドレスを格納する。
5行
整数型の変数 i を宣言し、 $ (11223344)_{16}$を代入している。
7行
変数 i の先頭アドレスをアドレス演算子 &により取り出し、ポインター p に代入している。
9行
整数変数 i の先頭アドレスを変換指定子 %p により表示している。
10行
ポインター p の先頭アドレスを変換指定子 %p により表示している。
12行
整数変数 i の値を16進数表示の変換指定子 %0x により表示している。
13行
ポインター p の値を16進数表示の変換指定子 %0x により表示してい る。ただし、ポインターはアドレスなので、強制型変換(キャスト)により、 符号なし整数にしている7
15行
ポインターが指し示すアドレスに格納されているデータを表示している。

   1 #include <stdio.h>
   2 
   3 int main(void){
   4   int *p;
   5   int i=0x11223344;
   6 
   7   p=&i;
   8 
   9   printf("address i %p\n", &i);
  10   printf("address p %p\n", &p);
  11 
  12   printf("value i %0x\n", i);
  13   printf("value p %0x\n", (unsigned int)p);
  14 
  15   printf("value *p %0x\n", *p);
  16 
  17   return 0;
  18 }
\fbox{実行結果}
	address i 0xbffff6b0
	address p 0xbffff6b4
	value i 11223344
	value p bffff6b0
	value *p 11223344

この実行結果から、メモリーは図7のようになっていること が分かる。ポインター p には、整数変数 i の先頭アドレスが格納されている。 さらに、ポインターpに間接参照演算子*を作用(*p)させることにより、 ポインターが指し示すアドレスの内容を取り出している。また、どんな変数でも、 アドレス演算子&で、メモリーのアドレスが取り出せている。これらのことをしっかり理解 すると、ポインターは難しくない。

図 7: リスト2のプログラム実行後のメモリーの内容
\includegraphics[keepaspectratio, scale=1.0]{figure/pointer_in_memory.eps}

3.3 ポインターに関係する演算子

ポインターに関係する演算子を表2にまとめておく。ただし、各変数は
	char   c, *cp;
	int    i, *ip;
	double x, *xp;
と宣言したとする。

表 2: 演算子
演算子 通常の変数(c, i, x) ポインター(cp, ip, xp)
  格納されている値 格納されているアドレス c, i, x, cp, ip, xp
& 変数のアドレス   ポインターのアドレス &c, &i, &x, &cp, &ip, &xp
* コンパイルエラーのため不可 ポインターが示す値 *cp, *ip, *xp

まとめると、重要なことは以下の通りである。

ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成19年6月24日


no counter