↗️
Goのポインタって何がいいの?
メリット
1. メモリや処理速度の向上
何かの処理を行う関数に引数として渡すとき、値渡しとポインタ渡しの2種類がある。値渡しの場合は、データをコピーして渡すのに対して、ポインタ渡しはアドレス(例:0xc00009e210
)を渡す。大きなデータ構造を引数にする場合、値私の場合はコピーするのにメモリや処理時間を消費してしまうが、ポインタを使用することでそのデメリットがなくなる。
package main
import "fmt"
type User struct {
name string
phone int
}
func main() {
u := User{name: "test", phone: 123456789}
printUser(u)
printPointerUser(&u)
fmt.Println("exit")
}
// 引数のuserはmain関数からコピーして渡されるので、Userのデータ構造が巨大な場合その分コストがかかる
func printUser(user User) {
fmt.Println(user)
}
// アドレスが渡される
func printPointerUser(user *User) {
fmt.Println(user)
}
2. 値の上書き
Goの仕様として、ポインタを使用することで関数内でデータの変更を呼び出しもとに反映させることができる。値渡しの場合、関数内での変更は呼び出し元の変更ができない(コピーされたのものの変更をするため)。
package main
import "fmt"
type User struct {
name string
phone int
}
func main() {
u := User{name: "test", phone: 123456789}
u.changeName()
// changeName関数でnameを"test" -> "aaaaaaaa"に変えたはずなのに、testのまま出力される
fmt.Println(u)
fmt.Println("========")
u.changePhone()
fmt.Println(u)
}
func (u User) changeName() {
u.name = "aaaaaaaa"
}
// 呼び出し元(main)変更を反映できる
func (u *User) changePhone() {
u.phone = 000000000
}
3. nil値を扱うことができる
ポインタを使用しなければ、初期化した際に各型の初期値(0や空文字、falseなど)が指定される。
package main
import "fmt"
func main() {
var a int
fmt.Println(a)
fmt.Println("========")
var b *int
fmt.Println(b)
}
デメリット
1. nilポインタ参照のリスク
ポインタがnilの状態で実体を参照しようとすると、nil pointer error
が発生する。
2. メモリ管理の負担
Goはガベージコレクション(不要になったメモリを解放する機能)を備えているが、ポインタを過度に使用すると不要なメモリ参照が残り、パフォーマンスの低下につながる。
3. 値のコピーよりも非効率になる場合がある
小さなデータ型をポインタで渡すと、かえってパフォーマンスが低下することがある。
Discussion