3 ポインター(10章)

3.1 ポインターとはなにか

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

アドレスを操作するとなると,アドレスを入れる変数が欲しくなる. 2.1.1節で述べたように,アドレスは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

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

図 6: リスト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年5月23日


no counter