<戻る  目次  進む>  
          <戻 進> 雑誌 白黒 明暗   フォント 既定 ゴシック 明朝 手書き   サイズ   標準  


   ポインタ変数を使ってみる   

  1. 第1項:ポインタ変数の宣言
  2. 第2項:アドレスを代入する
  3. 第3項:モードの切り替え
  4. 第4項:すなわちショートカット

[1]ポインタ変数の宣言

前節では、3種類のポインタに関する説明を行いましたので、
ここでは、実際にポインタ変数を宣言して、感覚をつかんでみたいと思います。

と言うわけで、早速ポインタ変数を宣言する例を示したいのですが、
実は、これがまたやっかいなシロモノだったりするのです。
とりあえず、intへのポインタ型の変数を宣言する例を2つ示します。


int *p;
int* p;
これが、多くの入門書で紹介されている、ポインタ変数の宣言の書き方です。
この2つは、pという名前のintへのポインタ型の変数を宣言する書き方です。

1つ目の書き方は*pという名前のようですが、*はポインタ型を意味する記号で、
実際には、int型変数のアドレスを記憶するpという変数を宣言しています。

それならば、型名に*の付く2つ目の宣言の方が読みやすくも思えるのですが、
2つ以上の変数を宣言すると、2つ目以降は見かけの型名と違ってしまいます。
次の例では、2つ目のp2は、普通のint型変数になってしまいます。


int* p1,p2;
深く考えるとややこしいので、ここでは1つ目の書き方で統一します。
つまり、変数名の前に*をつければ、ポインタ変数を宣言出来るのです。

目次に戻る


[2]アドレスを代入する

前節で説明した通り、ポインタ変数とは、アドレスを代入する変数です。
ポインタ変数の宣言の次は、早速アドレスを代入してみたいと思います。
ところで、アドレスを代入するのは良いとして、代入するアドレスはどうするのでしょうか。

理屈の上では、別にどんなアドレスでも代入出来るわけですが、
使えないアドレスを代入しても意味がありません。

実は、使えるメモリのアドレスを代入する方法があります。
方法は簡単なことで、もう1つ別の変数を宣言し、そのアドレスを代入する方法です。
次のプログラムは、ポインタ変数pに変数のアドレスを代入する例です。


int main(void)
{
	int *p,i;
	p = &i;
	return 0;
}
まず、変数名の前に*をつけるとポインタ変数として宣言出来るのだから、
変数名の前に*の付いていないiは普通の変数であることを理解して下さい。

この例では、&演算子で変数iのアドレスを求めてポインタ変数pに代入しています。
つまり、この段階で、ポインタ変数pにはiのアドレスが入っています。
と言うことは、iのアドレスとポインタ変数pの中身は、当然同じになるはずです。
次のプログラムは、printf関数でアドレスを表示して確かめる例です。


#include <stdio.h>

int main(void)
{
	int *p,i;
	p = &i;
	printf("p = %p\n",p);
	printf("&i = %p\n",&i);
	return 0;
}
このプログラムの実行結果は、次の通りになるかもしれません。

p = 0012FF80
&i = 0012FF80
見事に同じ値になっています。
このことは、ポインタ変数も変数であることからすれば、ある意味当然です。
だって、pに&iを代入して、直後にその値を表示しているのですから。

ただし、ここでは型に注意して下さい。
ポインタ変数pの型は、intへのポインタ型という型です。
変数iの型はint型ですが、&演算子を使って得られるアドレスはポインタ型です。
従って、&iをpに代入出来、また両方共に%p指定子で表示出来るのです。


[  ヌルポインタ  ]
ポインタ変数も宣言した直後はデタラメな値が代入されています。
その値が使用可能なアドレスなのかは全くわからないので、
勘違いでそのアドレスを使ってしまうと確実にバグになります。

これを防ぐにはアドレスを代入したかを区別する必要があります。
そこで、C言語には、ヌルポインタが用意されています。
NULL という記号をポインタ変数に代入しておけば、
アドレスが代入されていないことを示せます。


int *p = NULL;
このようにすれば、if文で p == NULL であるか比較すれば、
p にアドレスが代入されているか区別できます。

ちなみに、p = 0 としてもヌルポインタが代入されます。
これは文法として決まっていることであり、
NULL が 0 であるというわけではありません。

目次に戻る


[3]モードの切り替え

前節で説明した通り、ポインタ変数は、2モードを持っています。
それは、通常変数モードと、ポインタ変数モードです。
特に何も指定せずにポインタ変数を使っている場合はポインタ変数モードになります。

通常変数モードに切り替えるには、変数の前に*記号をつけます。
*記号がつけられたポインタ変数は、通常変数と全く同じ機能になります。
次のプログラムは、ポインタ変数を通常変数モードに切り替えて使う例です。


#include <stdio.h>

int main(void)
{
	int *p,i;
	p = &i;
	*p = 10; /* 通常変数モードに切り替えたポインタ変数に代入 */
	printf("*p = %d\n",*p);
	printf("i = %d\n",i);
	return 0;
}
このプログラムの実行結果は、次の通りになります。

*p = 10
i = 10
このプログラムでは、ポインタ変数pに*をつけて、通常変数モードに切り替えています。
*pは、通常変数モードに切り替わったポインタ変数pです。
*pである限りは、通常の変数と全く同じように扱うことが出来ます。

通常変数モードに切り替わったポインタ変数は通常の変数と同じように機能しますが、
その時使われるメモリは、ポインタ変数モードの時に代入されたアドレスです。つまり、

ポインタ変数モードの時に読み書きしたいメモリのアドレスを代入して、
その後、通常変数モードに切り替えてそのメモリを操作する。
と言うのが、ポインタ変数の最も基本的な使い方となります。

直接、何番のメモリを書き換えろ、と指定するのではなくて、
書き換えたいメモリのアドレスを代入し、モードを切り替えて書き換える、という、
いわば2段構になっているため、直感的にはわかりにくいかもしれません。

先ほどのプログラムでは、5行目でポインタ変数pに変数iのアドレスを代入し、
6行目で、pを通常変数モードに切り替えて、pが記憶したアドレスに10を代入しています。
この時、pが記憶したアドレスとは、つまりは変数iのアドレスなので、
結果として、変数iの値は10に書き換えられていることになります。

もう少し具体的に説明すれば、この時、変数iと通常変数モードの*pは、
全く同じメモリ領域を使っているということです。
*pに10を代入すると、iも自動的に10に切り替わると言うのではなく、
この2つはそもそも同じ場所を示しているのです。


[  奇怪な記号*  ]
*の記号は、実に3通りの意味を持っており、混乱の原因になります。
ここで、3つの区別をはっきりさせておきます。

1つ目は、乗算演算子です。いわゆる掛け算のことです。
式の中で使用する記号で、kai = 5 * 8 のようにして使用します。

2つ目は、間接参照演算子です。ポインタ変数を通常変数モードにします。
式の中で使用する記号で、*p のようにして使用します。
ポインタ変数モードの時のポインタ変数では掛け算が出来ないため、
乗算演算子と同じ記号を使っていても区別が付きます。

3つ目は、ポインタ変数を宣言する時に使用する記号です。
宣言の時にのみ使用され、int *p のようにして使用します。
ここがややこしいのですが、通常変数モードに切り替える間接参照演算子*と、
宣言の時に使用する*の記号は、何の関係もない全く別の記号です。
たまたま同じ文字を使っているだけのことに過ぎません。

この3つには全て別の文字を使う方がわかりやすいはずだと思います。
同じ文字を割り当てているのはC言語の欠陥の1つです。
でも、いまさら直しようがありませんから、
皆さんはこの3つが別の意味の記号であることをしっかり認識して下さい。

目次に戻る


[4]すなわちショートカット

前項までで、ポインタ変数の機能は全て説明しました。
実際、ポインタは、前項までで説明した通りの機能しか持っていません。
ポインタ変数モードの時にメモリのアドレスを代入して、
通常変数モードに切り替えてからそのメモリを操作する、これがポインタの全機能です。

ここまでを理解した上で、当然でてくる疑問があります。
結局の所、ポインタとは何の役に立つ機能なのでしょうか。
前項のように、ポインタ変数モードで変数のアドレスを代入して、
通常変数モードに切り替えて操作する、なんて面倒なことに何の意味があるのでしょう。

これはもう、疑問に思った通りで、そのような使い方では何の役にも立ちません。
普通に変数を操作した方が、よほど楽で間違いも少なくなります。

ポインタの本当の使い方は、ショートカットとして使用することです。
Windowsのデスクトップに並んでいる、あのショートカットと同じです。

ショートカットは、どこか別の場所にあるファイルを指し示すファイルです。
ショートカットを開けば、その指し示しているファイルが開かれます。
にも関わらず、ショートカットは指し示すファイル自体ではないので、
ショートカットはどこにでも自由に作ることが出来ますし、
複数個作ったり削除したりしても、指し示すファイルには何の影響もありません。

これこそが、まさにポインタの役割そのものです。
ポインタ変数に、実際に存在する変数のアドレスを記憶しておけば、
そのポインタ変数が使える場所であれば、元の変数が使えない場所であっても、
ポインタ変数を通常変数モードに切り替えれば、元の変数と同じく使うことが出来ます。
まさに、ショートカットのような働きをさせることが出来るわけです。


[  他言語のポインタ  ]
一般には、ポインタはC言語とC++のみの機能だと言われています。
確かに、指定したメモリのアドレスを操作するという意味ではその通りです。

しかし、ポインタの本当の使い方はショートカットとして使うことであり、
その観点ならば、実用的なほとんどの言語にポインタがあります
Javaの参照はまさしくそんな機能で、しかも頻繁に使われますし、
VisualBasicのSETステートメントなども同様と言って良いでしょう。

そもそも、ポインタがないのでは、連結リストや木構造などの、
複雑なデータ構造を実現出来ませんし、オブジェクト指向も困難です。
その意味では、仕組みが不明なJavaやVisualBasicのポインタより、
仕組みがはっきりしているC言語のポインタの方が理解しやすいです。

目次に戻る


<−前に戻る  先頭に戻る  次へ進む−>