値型と参照型とポインタ型の基本
| 分類 | 例 | 中身 | 代入の挙動 | 関数に渡すと |
|---|---|---|---|---|
| 値型 | int, struct, array | 値 | 値をコピー | コピーされる |
| 参照型 | slice, map, channel, function | 「背後のデータ」へのポインタ等を含む記述子 | 記述子をコピー(背後データは共有) | 共有される |
| ポインタ型 | *T | アドレス(参照) | アドレスをコピー | 元の値を操作できる |
値型
変数を代入したときに中身の値そのものがコピーされる型
package main
import "fmt"
func main() {
a := 10
b := a // 値コピー
b = 20
fmt.Println(a, b) // 10 20(aは変わらない)
}
参照型
中身を指す“アドレス”がコピーされる型
package main
import "fmt"
func main() {
a := []int{1, 2, 3} // スライス(参照型)
b := a // “アドレス”をコピー
b[0] = 99
fmt.Println(a, b) // [99 2 3] [99 2 3]
}
ポインタ型
ある変数のメモリアドレス(場所)を保持する型
package main
import "fmt"
func main() {
a := 10
p := &a // aのアドレスを取得(pは「ポインタ型」)
fmt.Println(a) // 10
fmt.Println(p) // 0x1400000xxxx(アドレス)
fmt.Println(*p) // 10(ポインタの先をたどって値を参照)
}
-
&a→ 「aのアドレス」を取得 -
p→ 「aを指すポインタ」 -
*p→ 「ポインタの先の値(aの中身)」を読む
ポインタ型と参照型と値型の使い分け
- slice/map/chan/func → 参照型
- append は“記述子が変わる”ので外に反映されない場合があります
- 値型(struct/数値)を“呼び出し元の実体”ごと変えたい場合 or 大きくてコピー避けたい場合 or nilを表したい場合
→ ポインタ型 - 上記以外 → 値のまま
まとめ
slice/map/chan/func は参照型なのでそのまま渡せば共有されます(ただし append は新しい記述子になり得るので結果は返します)。
struct や数値などの値型は、変更・サイズ・nil表現の必要があるときにだけ *T を使い、読み取りだけなら値で渡します。