← 記事一覧へ

Go 学習記録(10):ポインタと nil

Go 基礎学習の第 10 回。ポインタ、初期化、値の交換、nil を整理します。

ポインタ

ポインタは、別の変数のメモリアドレスを値として持つ変数です。

ポインタの大きさはアーキテクチャごとに固定され、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 を「何でも空になる値」と考えないようにします。

規則は Go の仕様の append、インデックス式、ゼロ値。

この記事の日付について

旧サイトに記載された日付を保持しています。実際に書き始めた時期は、これより早い可能性があります。

同じシリーズ · Go の基礎学習

前の記事Go 学習記録(9):構造体とメソッド ←次の記事Go 学習記録(11):インターフェース →