概要
Tour of Go の - Pointer についてのサンプル。
Go には、C言語などと同様にポインタが存在します。
ポインタは、対象の値のメモリアドレスを保持します。ゼロ値はnilです。
C言語とは違い、Go ではポインタ演算は出来ないようになっています。
使い方もC言語と同じです。
var ( i int p *int ) i = 100 p = &i fmt.Printf("i=%d\tp=%p\t*p=%d\n", i, p, *p) i = 200 fmt.Printf("i=%d\tp=%p\t*p=%d\n", i, p, *p) *p = 300 fmt.Printf("i=%d\tp=%p\t*p=%d\n", i, p, *p)
上記では、intの変数 i
と、intのポインタ p
を宣言して、i
のアドレスを p
に設定しています。
p
は、i
と同じアドレスを保持しているので、 i
の値を変更すると p
の指し示す先の値も変わっている。
同じ理屈で、p
の指し示す先の値を変更すると、i
の値も変わる。
i=100 p=0xc0000660a8 *p=100 i=200 p=0xc0000660a8 *p=200 i=300 p=0xc0000660a8 *p=300
ポインタを利用するのが多いパターンは、関数にデータを渡すときですね。
Goでは、値のコピーが発生するので、そのまま渡すと元の値をコピーした別のものが渡ります。
なので、渡した先の関数内で、そのデータの情報を変更しても、コピーに対して操作しているので元の値が変更されません。
そこで、ポインタを渡すことにより、ポインタの値(メモリアドレス)がコピーされて渡るので、指し示す先の値は同じものを操作できます。
また、巨大な構造体などをそのまま渡すとコピーすると、メモリ上でも処理上でもオーバヘッドがかかるので、そんなときもポインタを使ったりします。
var ( i int p *int f1 func(v int) f2 func(v *int) ) i = 100 p = &i f1 = func(v int) { v = 888 } f2 = func(v *int) { *v = 999 } fmt.Printf("i=%d\tp=%p\t*p=%d\n", i, p, *p) f1(i) fmt.Printf("i=%d\tp=%p\t*p=%d\n", i, p, *p) f2(p) fmt.Printf("i=%d\tp=%p\t*p=%d\n", i, p, *p)
i=100 p=0xc000064100 *p=100 i=100 p=0xc000064100 *p=100 i=999 p=0xc000064100 *p=999
自分で定義した構造体の場合も同様です。
type ( myType struct { Value int } ) var ( m myType p *myType f1 func(v myType) f2 func(v *myType) ) m = myType{ Value: 100, } p = &m f1 = func(v myType) { v.Value = 888 } f2 = func(v *myType) { v.Value = 999 } fmt.Printf("m=%v\tp=%p\t*p=%v\n", m, p, *p) f1(m) fmt.Printf("m=%v\tp=%p\t*p=%v\n", m, p, *p) f2(p) fmt.Printf("m=%v\tp=%p\t*p=%v\n", m, p, *p)
m={100} p=0xc00000a170 *p={100} m={100} p=0xc00000a170 *p={100} m={999} p=0xc00000a170 *p={999}
サンプル
package tutorial import "fmt" // Pointer は、 Tour of Go - Pointers (https://tour.golang.org/moretypes/1) の サンプルです。 func Pointer() error { // ------------------------------------------------------------ // Go言語のポインタ // // Go言語は、C や C++ と同様に ポインタ型 を持っている // 使い方もほぼ同じであるが、ポインタ演算は出来ないようになっている // // ポインタのゼロ値はnil. // ------------------------------------------------------------ //noinspection GoVarAndConstTypeMayBeOmitted var ( zeroPointer *int i int = 100 p *int = &i arr [2]int = [2]int{0, 0} sli []int = []int{0, 0} m map[int]int = map[int]int{1: 0, 2: 0} ) //noinspection GoNilness fmt.Printf("zeroPointer is nil? [%v]\n", zeroPointer == nil) showValue(i, p) updateValue(p, 200) showValue(i, p) updateValue(&i, 300) showValue(i, p) // 配列 fmt.Println(arr) updateArrayNotPointer(arr) fmt.Println(arr) updateArrayPointer(&arr) fmt.Println(arr) // スライス fmt.Println(sli) updateSliceNotPointer(sli) fmt.Println(sli) updateSlicePointer(&sli) fmt.Println(sli) // マップ fmt.Println(m) updateMapNotPointer(m) fmt.Println(m) updateMapPointer(&m) fmt.Println(m) return nil } func showValue(i int, p *int) { fmt.Printf("i=%d\tp=%p\t*p=%d\n", i, p, *p) } func updateValue(p *int, v int) { *p = v } func updateArrayNotPointer(arr [2]int) { // ポインタで渡していないので、値がコピーされて関数に渡る // なので、値を変更しても元の配列には影響しない arr[0] = 100 arr[1] = 200 } func updateArrayPointer(arr *[2]int) { // ポインタで渡しているので、参照(ポインタ)がコピーされた関数に渡る // なので、値を変更すると元の配列にも影響する // 本来であれば、arrは配列へのポインタなのでインデックス操作が出来ない // はずであるが、配列の場合、言語側で特殊措置が行われるため // ポインタであっても、そのままインデックス操作が行えるようになっている arr[0] = 100 arr[1] = 200 } func updateSliceNotPointer(sli []int) { // Go言語のスライスは、内部に参照先へのポインタを持っている構造になっている // なので、ポインタを取得して渡さなくても、値のコピー時に内部のポインタがコピーされるため // 結果として、同じ参照先を更新することになる。 sli[0] = 100 sli[1] = 200 } func updateSlicePointer(sli *[]int) { // わざわざスライスのポインタを取得すると、一旦 ポインタの実態 を取り出す手間が必要となる // スライスは、内部に参照先へのポインタを持っているため、ポインタ取得せずそのまま関数に渡しても問題ない (*sli)[0] = 300 (*sli)[1] = 400 } func updateMapNotPointer(m map[int]int) { // マップもスライスと同様に内部に参照先へのポインタを持っているので // わざわざポインタを取得して渡す必要はない m[1] = 100 m[2] = 200 } func updateMapPointer(m *map[int]int) { // スライスと同様に、マップのポインタからは直接 マップ のキー指定(インデックス指定) // が出来ないので、一旦デリファレンスをする必要がある (*m)[1] = 300 (*m)[2] = 400 }
try-golang/tutorial_gotour_13_pointer.go at master · devlights/try-golang · GitHub
実行すると以下な感じ。
[Name] "tutorial_gotour_pointer" zeroPointer is nil? [true] i=100 p=0xc000062100 *p=100 i=200 p=0xc000062100 *p=200 i=300 p=0xc000062100 *p=300 [0 0] [0 0] [100 200] [0 0] [100 200] [300 400] map[1:0 2:0] map[1:100 2:200] map[1:300 2:400]
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場