今川館

都内勤務の地味OLです

ポインタが分からない②

ポインタのことがやっぱり分からない。

どうしても納得いかないので、もうちょっと良く考えてみることにした。

以下のサンプルコードを動かして色々試してみると、どうも難しく考えるからいけないんだと思うようになった。

単なるルールとして、「ポインタを使うと同じものを指す」とだけ覚えておけば別に困らない気がしてきた。

package main

import (
	"fmt"
)

type Point struct {
	x int
	y int
}

func main() {
	var p = &Point{10, 20}
	var P = Point{3, 6}

	// これは、qがpそのものであることを直感的に示している。
	q := p

	// こちらも、(PとQは別物であることを前提に)、QがPそのものを指し示すよう指示しているように見える。
	Q := &P

	// これは、rとpが異なるものであることを直感的に示している。
	r := *p

	// ところが、これが、R !== P であることがわからない。
	R := P

	// つまり、この式でR === Pであるという前提を暗に抱いているといえる。
	// よって、「ポインタを代入しない限り、別物」から考え始めるのが良い。

	// sにはqのポインタを代入するので、s === q であることに疑いはない。
	s := &q

	// これは、p自体を操作しているようにしか見えない。
	(*p).x = 12

	S := *p; S.x = -100 // これをやってもpは一切影響を受けない。

	// これもまた、P自体を操作しているようにしか見えない。
	(&P).x = 45

	r.y = 17 // rにはポインタを取らなかったので、ここでpは影響を受けない。

	fmt.Println(p, q, r, *s) // &{12 20} &{12 20} {10 17} &{12 20}
	fmt.Println(P, Q, R)     // {45 6} &{45 6} {3 6}
}

どうもわたしが理解できていなかったことは、

  • &や*を付けると同じものや違うものを指すと誤解していた
  • そうではなくて、代入すると別々に分けられるタイミングが来るようだ
  • しかし、アドレス演算子を使ってポインタを代入すると、左辺と右辺が同一であることを示せる
  • (*p)や(&P)を使ってある変数そのものを操作できることから、やっぱり演算子自体にオブジェクトを分別する役割は無くて、代入に起因するとしか思えない。

ここまで考えるうちにつらくなってきて、要は「ポインタを使うと同じものを指す」とだけ覚えておけば良いんじゃないかと思うようになった。

元々、変数をポインタ型で宣言してしまうと、今度は「それ自身」を取り出す術がなくなるので、「*」という演算子が用意されている、と。

(まだよくわかってない・・)