C++によるプログラミング入門20
ポインタ

 こんにちは。今回はいよいよポインタです。初心者の方は「何がいよいよだよ」と思ったことでしょう。どうもすみません。これにはいわく因縁があるのです。なんちゃって。
 多くの人がC++のもとになったCという言語を勉強したのですが、同時に大量の挫折者もだしたようです。その挫折理由のナンバーワンがこのポインタだったと言われています。そいうお話があったのです。少しおどかしてしまいましたが、安心してください。本当はそんなに難しいものではありません。
 前に変数とは一般に整数などをいれておく「いれもの」だという話をしましたが、いれものに入れられた情報は、メモリ上のどこかに置かれるはずです。メモリが実際にどのように作られているかは、特別な人以外知らないわけですが、イメージだけは持っていましょう。それは、情報をいれておく箱の列のようなものです。そして、メモリの個々の箱には場所を表す番地がついているのです。これをアドレスといいます。このアドレスを格納する変数をポインタというのです。
 ちょっと、抽象的すぎますね。つまり、こんな感じです。私(小林)がコンピュータのメモリ内に住んでいたとしましょう(笑)。それは上で書いたメモリの(どこかの)箱の中です。そしてその箱には番号がついているわけです。私は複数の箱にまたがって住んでいるかもしれませんが、先頭の箱の番号が、私の住所(アドレス)ということになりますね。
下の例を見て、実行してください。説明は後でします。

//address_sample.cpp
#include <iostream>
using namespace std;

int main()
{
    int x = 3;
    cout << "xの値は" << x << endl;
    cout << "xのアドレスは" << &x << endl;
}

実行すると、たとえば、

のようになります。変数xのアドレス(メモリ上の場所、番地)は、&xと書かれるのです。そして、その値が1245064なのです。このアドレスはコンピュータがそのときそのとき勝手に決めるので、みなさんの出力は私のとは違っていると思いますが、上のような感じにはなったと思います。これがアドレスです。

まだ、ポインタという言葉はでてきていませんね。
実は、xのアドレスが、あとで必要になることもあります。この場合、このアドレスを格納しておく変数があるといいですね。それがポインタなのです。ポインタは何のアドレスを保持するか決めておかなければ定義できません。今の例では「int型変数のアドレス」を格納しておきたいわけです。「int型変数のアドレス」を格納する変数、つまりint型に対するポインタは一般に

    int* p;

と書かれます。ここで、変数名を「p」としましたが、これはどう変えても大丈夫です。そして、「p = &x;」とすれば、pにxのアドレスが代入されるわけです。この辺の話のサンプルを書くと、たとえば次のようになるでしょう。

//pointer_sample.cpp
#include <iostream>
using namespace std;

int main()
{
    int x = 3;
    int* p;
    p = &x;
    cout << "xの値は" << x << endl;
    cout << "xのアドレスは" << p << endl;
}

 実行結果は前のものと同じようになります。(アドレスの値は変わるかもしれませんが。)意味はわかったのではないでしょうか。もし、とりあえず、わかったと思ったら、整数型の変数を二つ用意して、そのアドレスを二つのポインタに格納して書き出すプログラムを書いてみてください。それは、

//pointer_sample2.cpp
#include <iostream>
using namespace std;

int main()
{
    int x = 3, y = 4;
    int* p1;
    int* p2;
    p1 = &x;
    p2 = &y;
    cout << "xのアドレスは" << p1 << endl;
    cout << "yのアドレスは" << p2 << endl;
}

なんてものでしょう。上のようなプログラムを自力で書けたなら優秀、そうでなくても上のプログラムの意味がわかれば今日は合格です。

 また、例えば、上のサンプル内で、「*p1」と書くと、それは「p1が格納しているアドレスにある変数」(それを、「p1が指し示す変数」などとも言います)という意味になります。今の場合、それはxですね。「*p2」と書くとどうでしょう。それはyです。考え込まないでください。とにかくこのように決められているのです。以下に例をあげます。

//pointer_sample3.cpp
#include <iostream>
using namespace std;

int main()
{
    int x = 3, y = 4;
    int* p1;
    int* p2;
    p1 = &x;
    p2 = &y;
    cout << "xのアドレスは" << p1 << endl;
    cout << "p1が指し示す変数(つまりx)の値は" << *p1 <<endl;
    cout << "yのアドレスは" << p2 << endl;
    cout << "p2が指し示す変数(つまりy)の値は" << *p2 << endl;
}

 ポインタの説明はこれでほぼ終わりです。どうですか?わかりましたか?
はじめてポインタを見たとき、私が思ったことは「これが何の役に立つのか?」という疑問でした。その疑問は膨らんで、自分は何をやっているんだろう、という不安につながっていきました。今そう思っている人も多いのではないでしょうか。
今私に言えることは、C++をやっている限り、ポインタは避けて通れないということと、慣れればどうということはないということです。これは何なんだろう、どうやって使いこなすのだろうと悩まずに、「次にまた出てきたときに考える」という風に大きく構えていた方がよいと思います。(大きく、は変かな?)何の役に立つかは、次回にとても大事な例をお見せしますので、それまで、待ってください。

 それと、ちょっと面倒な話題ですが、書き方についてのコメントがあります。

    int* p1;

    int *p1;

と書いても同じです。下の書き方だと、*がp1にくっついて見えて、どれが変数なんだろうと、迷うかもしれませんが、これでもp1の定義なのです。「p1が指し示す変数」という意味で「*p1」というものを紹介しましたが、これは「p1を使うときの使い方」です。上はあくまで「p1の定義」なので、別物と考えてください。
 また、
    int *p1;
    int *p2;
は、まとめて、

    int *p1, *p2;

と書けます。

    int* p1, p2;

と書くとp2はポインタにならない(という規則があります)ので注意してください。


 アドレスはint型変数などだけでなく、クラスのインスタンス(オブジェクト)にもあります。そして、「クラスのインスタンス(オブジェクト)のアドレスを格納する変数」、つまり「インスタンス(オブジェクト)を指し示すポインタ」も、int型のときと同様に定義でるのです。以下に例をあげます。

//pointer_neko.cpp
#include <iostream>
#include <string>
using namespace std;

class Neko
{
    string name;
public:
    Neko(string); 
    void naku() const;
};

Neko::Neko(string s) : name(s){}

void Neko::naku() const{
    cout<<"にゃあ。俺様は"<<name<<"だ。"<<endl;
}

int main()
{
    Neko dora("ボス");
    //Nekoのインスタンスを指し示すポインタpcat
    Neko* pcat;


    pcat = &dora;
    dora.naku();
    pcat->naku();
}

 ここでpcatがポインタです。
 実行すると、ボスという名の猫が2度鳴いて終わりです。一度目はもちろんdora.naku()です。2度目の方は pcat->naku()ですが、説明しましょう。まず、pcatは「Nekoのインスタンスを指し示すポインタ」です。(これを「Nekoのインスタンスへのポインタ」とも言います。まあ、言い方の問題ですが。)これはintの例と同じ形ですね。そこに「pcat = &dora;」で、doraのアドレスを代入しています。したがって、pcatも実質的にはdoraと同じものを表しているのです。ただ、doraがオブジェクトそのものを表すのに対して、pcatはポインタです。そこにわずかな違いがあります。
 さて、doraのnaku()を呼び出すにはピリオド「.」を使って、「dora.naku();」とすればよかったのですが、pcatはポインタなので、同じやり方でnaku()を呼び出すことはできません。pcatを通してnaku()を呼び出すには

    (*pcat).naku();

とすればよいのです。「*pcat」が「pcatが保持するアドレスにある変数」つまりdoraを表すからです。しかし、もっと簡単に、「->」を使って、

    pcat->naku();

で、同じことができるようになっています。これで、「pcatが指し示すオブジェクト」(つまりdora)のnaku()を呼び出すことになるのです。
 このように、ポインタが指し示すオブジェクトのメンバを呼び出すときには、「->」が使われることを覚えておいてください。

 今日はつくづく、何の役に立つのか、という例ばかりあげました。実際、今日のプログラムはどれもポインタの説明用ですので、自分で書くプログラムの参考にはしないでくださいね。(^^;)ポインタのちゃんとした使い方や重要性の説明は難しいのですが、たとえばnewやdeleteという演算子を使うためにはどうしても必要なものです。これらについては、次回に説明します。どうか、今回の内容だけで、ポインタを判断しないでください。


というわけで、次回はnewとdeleteです。
目次のページ
前のページ  後のページ