ポインターと関数
値渡しとポインター渡しの違い
「ポインターと関数」はCoddyKit上の無料Go Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはGo Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Go Academyコースには全4レッスンが含まれています。
値渡し
Goでは、すべての関数引数が値渡しされ、コピーが作成されます。関数内で変更しても元の値には影響しません。
package main
import "fmt"
func increment(n int) { n++ }
func main() {
x := 5
increment(x)
fmt.Println(x) // 5 — unchanged
}ポインタ渡し
関数内で元の値を変更できるように、ポインタを渡します:
package main
import "fmt"
func increment(n *int) { *n++ }
func main() {
x := 5
increment(&x)
fmt.Println(x) // 6
}ポインタを返す
関数は、ヒープに割り当てられた値へのポインタを返せます。Goのエスケープ解析によって安全性が確保されます:
package main
import "fmt"
func newInt(v int) *int { return &v }
func main() {
p := newInt(42)
fmt.Println(*p) // 42
}大きな構造体の効率化
大きな構造体をポインタで渡すと、コストの高いコピーを避けられます:
package main
import "fmt"
type BigData struct{ Data [1024]int }
func process(d *BigData) { d.Data[0] = 99 }
func main() {
bd := BigData{}
process(&bd)
fmt.Println(bd.Data[0]) // 99
}インターフェースへのポインタ
ほとんどの場合は必要ありません。インターフェースは内部にすでにポインタを保持しています。*interface{}を渡すのは、ほぼ常に誤りです:
package main
import "fmt"
type Stringer interface{ String() string }
func print(s Stringer) { fmt.Println(s.String()) }
// Pass interface directly, NOT *Stringer
type Name string
func (n Name) String() string { return string(n) }
func main() {
print(Name("Alice"))
}ポインタによる省略可能なパラメーター
ポインタパラメーターを使うと、「指定されていない」状態とゼロ値を区別できます:
package main
import "fmt"
func greet(name *string) {
if name == nil {
fmt.Println("Hello, stranger")
return
}
fmt.Println("Hello,", *name)
}
func main() {
s := "Alice"
greet(&s)
greet(nil)
}スライスを介した変更
スライスには内部ポインタがすでに含まれているため、多くの場合*[]Tは必要ありません。ただし、appendによって再割り当てが発生する場合は、スライス自体へのポインタが必要です:
package main
import "fmt"
func addItem(s *[]int, v int) { *s = append(*s, v) }
func main() {
nums := []int{1, 2, 3}
addItem(&nums, 4)
fmt.Println(nums) // [1 2 3 4]
}ポインタのエイリアシング
同じ変数を指す複数のポインタを作るとエイリアシングが発生します。一方のポインタを介した変更は、ほかのすべてのポインタからも見えます:
package main
import "fmt"
func main() {
x := 10
a, b := &x, &x
*a = 99
fmt.Println(*b) // 99 — same memory
}ポインタの使い過ぎを避ける
ポインタは必要な場合にだけ使ってください:
- 関数内で値を変更する場合
- 大きな構造体(>= 約64バイト)
- nilで「存在しない」ことを表す場合
ポインタを使い過ぎるとGCへの負荷が増え、コードの明瞭さが低下します。
関数ポインタパターン
ディスパッチテーブル用に、関数ポインタをマップに格納します:
package main
import "fmt"
func add(a, b int) int { return a + b }
func sub(a, b int) int { return a - b }
func main() {
ops := map[string]func(int,int)int{
"+": add, "-": sub,
}
fmt.Println(ops["+"](10, 3)) // 13
fmt.Println(ops["-"](10, 3)) // 7
}確認問題
呼び出し元で定義された整数変数を関数から変更できるようにするには、どうすればよいですか?
まとめ
重要なポイント:
- Goは値渡しです。変更するにはポインタを使います
- ヒープに割り当てるコンストラクターからポインタを返します
- スライスは内部ポインタを持ちますが、
appendには*[]Tが必要になる場合があります - ポインタの使い過ぎを避け、小さな型では値を優先します
練習課題
*[]stringを受け取り、strings.ToLowerを使ってすべての文字列をその場で小文字に変換するnormalize関数を作成してください。
よくある質問
「ポインターと関数」レッスンは無料ですか?
はい。「ポインターと関数」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Go Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Go Academyコースには全4レッスンが含まれています。
「ポインターと関数」で何を学びますか?
値渡しとポインター渡しの違い ブラウザで直接実行するハンズオンコードでGo Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Go Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのGo Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「ポインターと関数」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このGo Academyレッスンでコードを書いて実行できますか?
はい。すべてのGo Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。