アドレスを操作するとなると、アドレスを入れる変数が欲しくなる。 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;のようにして代入できる。アドレス演算子(&)により変数の先頭アドレスを取り出 して、代入演算子(=)を用いて、ポインター型変数に代入している。
ポインター機能は、アドレスの格納のみに止まらず、そのアドレスが示しているデータの 内容も表すことができる。今までの例の通り、ポインターには変数の先頭アドレスが格納 されている。そして、ポインターの宣言の型から、そのポインターが指しているデータの 内容までたぐり寄せることができる。ポインターpiとpxが示しているデータの 値を、整数型変数 j と実数型変数 y に代入する場合
j=*pi; y=*px;とかく。ここで、アスタリスク(*)は間接参照演算子で、ポインターが示しているア ドレスのデータを取り出せるのである。このようにアドレスのみならず、そのアドレスの データの型までポインターは持っているから、これが可能なのである。このことから、アドレ スとは言わずにポインター(pointer 指し示すもの)と言うのであろう。
このプログラムの各行の内容は、以下の通りである。1行毎にきっちり理解することが重 要である。
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 }
address i 0xbffff6b0 address p 0xbffff6b4 value i 11223344 value p bffff6b0 value *p 11223344
この実行結果から、メモリーは図7のようになっていること が分かる。ポインター p には、整数変数 i の先頭アドレスが格納されている。 さらに、ポインターpに間接参照演算子*を作用(*p)させることにより、 ポインターが指し示すアドレスの内容を取り出している。また、どんな変数でも、 アドレス演算子&で、メモリーのアドレスが取り出せている。これらのことをしっかり理解 すると、ポインターは難しくない。
char c, *cp; int i, *ip; double x, *xp;と宣言したとする。