これまでの話で,メモリーというものが大体分かったと思う.そして,その内容とともに,
アドレスが重要であることも分かったであろう.あるいは,アドレスを上手に操作すれば,
いろいろなこと(悪いことも)ができそうだ--ということも分かったであろう.
アドレスを操作するとなると,アドレスを入れる変数が欲しくなる.
2.3節で述べたように,アドレスは32ビットである.また,
int型のデータも32ビットである.従って,int型の変数にアドレスを入れるこ
とがあできそうである.具体的には,hogeと言う変数のアドレスをint型の変
数 i に,次のような文で,
i=&hoge;
と代入する.しかし,これはコンパイラーにより警告が出され,推奨される方法でない
6.たまたま,
私が使っているコンパイラーでは警告で済んでいるが,エラーを出すものもあるであろう.
そもそも,アドレスのビット数と
int型のビット数が同じであるのは偶然にすぎない.
幸いなことに,C言語にはアドレスを格納する仕組みが用意されている.アドレスを格納
するための変数をポインター変数7という.それは,
int *pi;
double *px;
と宣言する.アスタリスク(
*)をつければ,ポインター変数の宣言になる.
変数のアドレスは,
アドレス演算子(
&)により取り出すことができる.たと
えば,整数型変数
i と実数型変数
x のアドレスは,
&iと
&xとすると取
り出すことができる.ここで,間接参照演算子
&は,それに引き続く変数の先頭ア
ドレスを取り出す演算子である.取り出したアドレスは,ポインターに
pi=&i;
px=&x;
のようにして代入できる.アドレス演算子(
&)により変数の先頭アドレスを取り出
して,代入演算子(
=)を用いて,ポインター変数に代入している.
ポインター機能は,アドレスの格納のみに止まらず,そのアドレスが示しているデータの
内容も表すことができる.今までの例の通り,ポインター変数には変数の先頭アドレスが格納
されている.そして,ポインターの宣言の型から,そのポインターが指しているデータの
内容までたぐり寄せることができる.ポインター
piと
pxが示しているデータの
値を,整数型変数
j と実数型変数
y に代入する場合
j=*pi;
y=*px;
と書く.ここで,アスタリスク(
*)は
間接参照演算子8で,ポインターが示しているア
ドレスのデータを取り出す演算子である.このようにアドレスのみならず,そのアドレス
のデータの型までポインターは持っているから,これが可能なのである.このことから,
アドレスとは言わずにポインター(pointer 指し示すもの)と言うのであろう.
まったくもって,紛らわしいことに,間接参照演算子と積の演算子は同じアスタリスク
(*)をつかう.C言語の悪いところだが,そうなってしまっているので仕方ない.多
分,コンパイラーは前後の式からどちらなのか判断しているのだろう.
-4pt
- ポインター変数は,アドレスを格納する変数である9.
- ポインターの宣言には,型名とアスタリスク(*)を付ける.
- 変数のアドレスを取り出すには,変数名の前にアンパサンド(&)をつける.
&はアドレス演算子である.
- ポインターが示しているデータの値を取り出すためには,ポインター変数の前に
アスタリスク(*)をつける.*は間接参照演算子である.
リスト
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 }
address i 0xbffff6b0
address p 0xbffff6b4
value i 11223344
value p bffff6b0
value *p 11223344
この実行結果から,メモリーは図7のようになっていること
が分かる.ポインター p には,整数変数 i の先頭アドレスが格納されている.
さらに,ポインターpに間接参照演算子*を作用(*p)させることにより,
ポインターが指し示すアドレスの内容を取り出している.また,どんな変数でも,
アドレス演算子&で,メモリーのアドレスが取り出せている.これらのことをしっかり理解
すると,ポインターは難しくない.
図 7:
リスト2のプログラム実行後のメモリーの内容
|
ポインターに関係する演算子を表
2にまとめておく.ただし,各変数は
double x, *xp;
と宣言したとする.
表 2:
普通の変数とポインター変数に演算子を作用させた場合に取り出せる値.
&:アドレス演算子.*:間接参照演算子
演算子 |
通常の変数(x) |
ポインター変数(xp) |
例 |
無し |
格納されている値 |
格納されているアドレス |
x,xp |
& |
変数のアドレス |
ポインター変数のアドレス |
&x,&xp |
* |
コンパイルエラーのため不可 |
ポインターが示すアドレスに格納されている値 |
*xp |
まとめると,重要なことは以下の通りである.
-4pt
- 通常の変数には値が,ポインターにはアドレスを格納する.
- アドレス演算子&は,それに引き続く変数(ポインター変数も含む)のア
ドレス返す.
- 間接参照演算子*は,それに引き続くポインター変数が指し示すアドレスの値を返す.
ホームページ:
Yamamoto's laboratory著者:
山本昌志
Yamamoto Masashi
平成19年2月9日