3 ポインターとなにか

3.1 ポインター変数と演算子

3.1.1 ポインター変数

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

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

	i=&hoge;

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

幸いなことに,C言語にはアドレスを格納する仕組みが用意されている.アドレスを格納 するための変数をポインター変数7という.それは,

	int *pi;
	double *px;

と宣言する.アスタリスク(*)をつければ,ポインター変数の宣言になる.

3.1.2 アドレス演算子

変数のアドレスは,アドレス演算子(&)により取り出すことができる.たと えば,整数型変数 i と実数型変数 x のアドレスは,&i&xとすると取 り出すことができる.ここで,間接参照演算子&は,それに引き続く変数の先頭ア ドレスを取り出す演算子である.取り出したアドレスは,ポインターに
	pi=&i;
	px=&x;

のようにして代入できる.アドレス演算子(&)により変数の先頭アドレスを取り出 して,代入演算子(=)を用いて,ポインター変数に代入している.

3.1.3 間接参照演算子

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

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

まったくもって,紛らわしいことに,間接参照演算子と積の演算子は同じアスタリスク (*)をつかう.C言語の悪いところだが,そうなってしまっているので仕方ない.多 分,コンパイラーは前後の式からどちらなのか判断しているのだろう.




-4pt

3.2 プログラム例

リスト2に示すプログラムで,ポインターの意味とそれに関 わる演算子の動作の基礎的なことを理解しよう.このプログラムの各行の内容は,以下の 通りである.1行毎にきっちり理解することが重要である. -4pt
5行
整数型のポインター変数pを宣言している.変数pは整数型のデータの先頭 アドレスを格納する.
8行
変数iの先頭アドレスをアドレス演算子&により取り出し,ポイン ター変数pに代入.
10行
整数型変数iの先頭アドレスを変換指定子%pにより表示している.
11行
ポインター変数pの先頭アドレスを変換指定子%pにより表示している.
13行
整数型変数iの値を16進数で表示している.変換指定子%0xを指定する とデータは16進数表示になる.
14行
ポインター変数pの値を16進数表示の変換指定子%0x により表示してい る.ただし,ポインターはアドレスなので,強制型変換(キャスト)により, 符号なし整数にしている10
16行
ポインターが指し示すアドレスに格納されているデータを表示している.

   1 #include <stdio.h>
   2 
   3 int main(void)
   4 {
   5   int *p;
   6   int i=0x11223344;
   7 
   8   p=&i;
   9 
  10   printf("address i %p\n", &i);
  11   printf("address p %p\n", &p);
  12 
  13   printf("value i %0x\n", i);
  14   printf("value p %0x\n", (unsigned int)p);
  15 
  16   printf("value *p %0x\n", *p);
  17 
  18   return 0;
  19 }
\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にまとめておく.ただし,各変数は
	double x, *xp;

と宣言したとする.

表 2: 普通の変数とポインター変数に演算子を作用させた場合に取り出せる値. &:アドレス演算子.*:間接参照演算子
演算子 通常の変数(x) ポインター変数(xp)
無し 格納されている値 格納されているアドレス x,xp
& 変数のアドレス   ポインター変数のアドレス &x,&xp
* コンパイルエラーのため不可 ポインターが示すアドレスに格納されている値 *xp

まとめると,重要なことは以下の通りである.
-4pt

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


no counter