ポインタ
ポインタは、別の変数のメモリアドレスを値として持つ変数です。
ポインタの大きさはアーキテクチャごとに固定され、32 ビットでは 4 バイト、64 ビットでは 8 バイトです。
1. ポインタの宣言と利用
アドレスを取得する演算子:&
ポインタ型と間接参照の演算子:*
アドレスを渡し、ポインタで受け取ります。
ポインタを宣言する:
var a int = 10
var p *int = &a
// 定义了一个指针,p的类型为*int,p指向a的地址
//通过指针修改值
*p = 20
fmt.Println(a) //20
通常の Go のポインタは C/C++ と異なり、ポインタ演算による他のメモリへのアクセスを認めません。
unsafe パッケージにはアドレスを操作する機能がありますが、通常の安全性を回避する操作なので、安易に使うべきではありません。
2. ポインタを初期化する
未初期化のポインタのゼロ値は nil です。
間接参照の前に、有効な値を指していることを確認します。
初期化の書き方には次のような選択肢があります。用途に応じて選び、すべてを同じスコープに貼り付けないでください。
var a int = 10
var p *int = &a
var p2 = new(int) //new函数返回一个指针,指向一个新的int类型的值,值为0
// 如果是结构体还能用下面的方式
p := &Person{}
既存の変数のアドレスは &、新しいゼロ値のオブジェクトは new、構造体なら &Person{} のような書き方も使えます。
map、slice、channel の初期化には make を使います。
間接参照には有効な参照先が必要です。nil の map は読み取れますが、書き込む前に初期化しなければなりません。
3. ポインタで値を交換する
func swap(a, b *int) {
t := *a
*a = *b
*b = t
}
func main() {
var a int = 10
var b int = 20
swap(&a, &b)
fmt.Println(a, b) //20 10
}
4. Go の nil
nil はポインタ、スライス、map、channel、関数、インターフェースなどのゼロ値です。すべての型に共通するゼロ値ではなく、整数は 0、真偽値は false、構造体は各フィールドのゼロ値になります。
間違えやすい点は二つです:nil のスライスには append できる。nil の map は読めるが、そのままでは書けない。
package main
import "fmt"
func main() {
var nums []int
nums = append(nums, 1)
fmt.Println(nums) // [1]
var counts map[string]int
fmt.Println(counts["go"]) // 0
// counts["go"] = 1 // nil map 写入会 panic
counts = make(map[string]int)
counts["go"] = 1
fmt.Println(counts["go"]) // 1
}
空のスライスと nil のスライスは、どちらも長さ 0 になり得ますが、slice == nil の結果は異なります。また、スライス同士は整数のように == で直接比較できません。
型のある nil ポインタを格納したインターフェースは、インターフェース自体が nil とは限りません。error を返すときにも注意が必要です。nil を「何でも空になる値」と考えないようにします。