コンテンツにスキップ

Go/ポインタ

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go

Goにおけるポインタの特徴と用例

[編集]

ポインタの基本的な特徴

[編集]

1. シンプルな構文

[編集]

Goのポインタは、C/C++と比較して非常にシンプルです:

  • *T はT型へのポインタ
  • & は変数のアドレスを取得
  • * はポインタの参照先の値にアクセス

2. ポインタ演算がない

[編集]

C/C++と異なり、Goではポインタ演算(ポインタの加算・減算)ができません。これによりメモリ安全性が向上しています。

3. nil値

[編集]

初期化されていないポインタはnilになります。nilポインタへのアクセスはランタイムパニックを引き起こします。

主な用例

[編集]

用例1: 関数で値を変更する

[編集]
package main

import "fmt"

// 値渡し - 元の値は変更されない
func incrementByValue(x int) {
    x++
}

// ポインタ渡し - 元の値が変更される
func incrementByPointer(x *int) {
    *x++
}

func main() {
    num := 10
    
    incrementByValue(num)
    fmt.Println("値渡し後:", num) // 10(変更なし)
    
    incrementByPointer(&num)
    fmt.Println("ポインタ渡し後:", num) // 11(変更あり)
}

用例2: 大きな構造体の効率的な受け渡し

[編集]
type LargeStruct struct {
    data [1000]int
    name string
}

// 値渡し - 構造体全体がコピーされる(非効率)
func processByValue(s LargeStruct) {
    // 処理
}

// ポインタ渡し - アドレスのみコピー(効率的)
func processByPointer(s *LargeStruct) {
    // 処理
    s.name = "Updated" // ドット演算子でアクセス可能
}

用例3: メソッドレシーバ

[編集]
type Counter struct {
    count int
}

// 値レシーバ - 元の構造体は変更されない
func (c Counter) IncrementValue() {
    c.count++
}

// ポインタレシーバ - 元の構造体が変更される
func (c *Counter) IncrementPointer() {
    c.count++
}

func main() {
    counter := Counter{count: 0}
    
    counter.IncrementValue()
    fmt.Println(counter.count) // 0
    
    counter.IncrementPointer()
    fmt.Println(counter.count) // 1
}

用例4: nilチェック

[編集]
func processUser(user *User) {
    if user == nil {
        fmt.Println("ユーザーが存在しません")
        return
    }
    fmt.Println(user.Name)
}

用例5: new関数による割り当て

[編集]
// newはゼロ値で初期化されたポインタを返す
p := new(int)
fmt.Println(*p) // 0

user := new(User)
// userは*User型、フィールドはゼロ値で初期化される

ポインタを使うべきタイミング

[編集]
  1. 関数内で値を変更したい場合
  2. 大きな構造体を扱う場合(コピーコストを避ける)
  3. nilを表現したい場合(値が存在しない状態)
  4. インターフェースを実装する場合(ポインタレシーバが必要なことが多い)

注意点

[編集]
  • スライス、マップ、チャネルは参照型なので、通常ポインタを使う必要はありません
  • 小さな構造体(数バイト)の場合、値渡しの方が効率的な場合があります
  • ポインタレシーバとバリューレシーバを混在させるのは避けるべきです

Goのポインタは、安全性を保ちながら効率的なメモリ管理を実現する重要な機能です。