2013/07/09

Pointerについて

C/C++のポインタについて、もっかい纏めておく。
ポインタが難しいとか言う話よりも「オート変数を戻り値に使えない」という話の方が重要で簡単だと思う(まぁどの話でピンと来るかは人それぞれだけど)。

メモリ


スタック領域:ローカル変数、オート変数と呼ばれる。高速、容量は少ない(PCで動かすプログラムならそんなに気にすることもない)。ローカル変数なので関数を抜けると勝手に消える。なのでスタック領域へのポインタを戻り値としては使えない。(スタックという名称から想像出来る)
ヒープ領域:new(alloc関数)で確保する、遠くの大容量メモリ。遠くのメモリなので(ほんの少し)低速だが大容量。遠くのメモリなので勝手には消えない。戻り値に使うならこっち。ただし勝手に消えないので誰かがdeleteする必要がある。
int * getIntPointer()
{
    int i = 5; // ローカル変数でポインタでない。スタック領域を確保してる。
    int * pi = NULL// ポインタ、まだ領域確保してない。

    pi = &i; // ポインタpiは、iが確保してるスタック領域へのアドレス。
 
    return pi;
}
スタック領域を指してるポインタは関数を抜けると同時に消されても文句言えない。
なのでこのポインタを返すのは誤り。
int * getIntPointer()
{
    int * pi = NULL// 同じく、まだ領域確保してないポインタ。
    pi = new int(5); // ヒープ領域にintの5を確保し、そのアドレスをpiに入れた。

    return pi;
}
ヒープ領域に確保したメモリのポインタなので関数を抜けても消されない。呼出し元で戻り値を使うことが出来る。
代わりに使い終わった後のdeleteは呼出し元でやってね。

*と&


しっかりと理解している分けではない、というか覚えてない、というか、動くコードが作れればいい、というかもう無茶苦茶。
 int * pi;
 int i = 5;
 &i; // &iでiのアドレス値を取得。
 i;  // iでは変数の値を取得(というかまんま)。

 pi = &i;

 *pi; // piの指す所の値を取得。
 pi;  // pi と書いただけではアドレス値でしかない。

 // 直の変数なら&でアドレス取得、ポインタなら*でアドレス取得

 // ↓関係ないがw
 j = 2 * 5; // 2x5 の意味。10。
 bool b = true & false// 論理演算。この場合はfalse。ORは「|」。

C/C++にはプリミティブ型とオブジェクト型、というような区別は無いらしいが、Javaで言うプリミティブ型はアドレス値、実際の値、と区別しないと面倒なことになる。
しかし、オブジェクト型の場合はもうちょっと話が変わる(様な気がする)。
   Person * p = new Person("Jhon Smis");
   p->getName();
   p->getAge();

   Person p("Jhon Smis");
   p.getName();
   p.getAge();

上がポインタを利用した例。下がオート変数の例。オブジェクトへのポインタで、関数呼び出しをするのは「->」(変な演算子)。ポインタじゃなくてオブジェクトを直にスタックに確保し(これはJavaでは無い概念)、その関数を呼び出す場合は「.」。
あとnew演算子を使わないでもメモリに領域確保される、というのもJavaではありえないことなので驚愕するだろう。変数名に直に括弧を付けてコンストラクタ呼出し、という文法もJavaでは無いので少し驚く。

実感としてメモリの場所の違いがイメージできれば、int型のポインタなんて使い道があまり無いってことと、オブジェクト型のポインタは関数呼び出しの演算子が「->」に変わる、って理解で大抵の場合はこと足りる。
この辺を頭に置いとくとかなりギャップが小さくなるかと思う。

ギャップについて、こんな記事を見つけた。
C++を使い慣れた方からすれば、あり得ないような誤解もあります。
例えば、int 型や double 型のようなC言語にもあるような型はともかく、
クラス型のオブジェクトは常に new で動的に割り付ける必要があるとい
うものです。おそらく、Javaか何か別の言語からの類推によるものでしょう。 
つまり逆の立場から書いてるんですが、「あり得ないような誤解」ですかぁ…
Java的には当たり前すぎて間違ってることに気付きもしなかったです

0 件のコメント:

コメントを投稿