Golangのポインタの基本

golang ポインタ 値渡し 参照

プログラミング

2018-09-03 12:48:54

概要

Golangのポインタ基礎についてまとめる。

前提 

変数とメモリの関係についてイメージできる程度の知識

ポインタとは何か

ポインタは変数のアドレスを指す。
変数のアドレスを通じて呼び出し元の変数の値を変更することができる。

GolangではC言語ライクなポインタは用意されている。
Golangでのポインタでは、変数Tのポインタは*T型で、ゼロ値はnilとなる。

package main

import "fmt"

func main() {
  i := 10

  // &(アドレス演算子)を使い、変数のアドレスにアクセスする
  // 変数pointerに変数iのアドレスを格納
  pointer := &i

  // アドレスが同じことを確認する
  fmt.Println(&i)
  fmt.Println(pointer)

  // ポインタを示す変数pointerから*を使って値を取り出す
  // 呼び出し元の変数iの値が変更されていることを確認
  *pointer = 100
  fmt.Println(*pointer) // 100
  fmt.Println(i) // 100に変更されている
}

C言語ではポインタ演算があるが、Golangではない。

値渡し、参照渡しとの違い

値渡し

関数に変数を引数として渡すときの方法のひとつ。
値渡しは変数の値は別のアドレスにコピーされる。
別のアドレスにコピーされるため呼び出し元の変数に影響はない。

package main

import (
  "fmt"
)

func foo(x int) (int) {
  return x
}

func main() {
  x := 1

  foo(x) // 値渡し 変数xの値がfoo関数の仮引数xに別アドレスとしてコピーされる
  fmt.Println(x) // 10
}

Golangでは関数への引数の渡し方は基本的には値渡しだが、スライスやマップを使うときはこの限りではない。(スライスの場合、スライスの変数がスライス自体ではなくスライスへの参照を保持しているため)

参照渡し

参照渡しされて変数は、呼び出し元の変数の値、アドレスすべて同じになる。
別アドレスに変数の値をコピーする値渡しと異なり、コピーではないための呼び出し元の関数に影響がある。
ポインタとの違いは、ポインタはアドレスと値の操作を別々に行うのに対し、参照はアドレスも値も同時に操作する点。
Golangには参照渡しの概念はあるが、参照渡しの書き方はないらしい。
参照型が参照渡しの挙動をする。

まとめ

&でアドレスを渡す、*でアドレスを渡された変数から値を取り出す。

所感

より深く理解するには、変数とメモリの関係についての深掘りとC言語のポインタを学び直すのが良さそう。
値渡し、参照渡し、ポインタそれぞれについてメモリがどのようにデータを扱うかイメージを持っておくと理解しやすい。
構造体を扱う際にポインタを理解しておく必要がありそうなので、ちゃんと勉強しておく。