概要
- 前回の続き
- 今回はポインタについて以下を学習
- 変数からポインタの参照方法や、ポインタから値の参照法について
- ポインタとミュータブルの関係について
- マップやスライスが値渡しでない理由
参考
ポインタについて
- ポインタは参照先のアドレス値のこと。
- リテラル値とは別のメモリ空間で管理されている
- C言語とは異なり、ポインタの操作はデフォルトでは許可されていない
ポインタ値の参照、逆参照
- ポインタ値は、
&変数
で取得する - ポインタ値が参照している実際の値は、
*ポインタ変数
で取得する - ポインタ型の変数をvarで宣言するには
*型
で行う - ポインタの参照先がnilの場合、値は設定できない。そのため、ゼロ値と値なしを区別する際に利用することもある。(MapのカンマOkイディオムを使うほうがよいが。)
x := "hello"
ponterX := &x //ポインタ値の取得
x2 := *ponterX // ポインタ値から値の取得(逆参照)
fmt.Println(ponterX) //0x1400010a230(アドレス)
fmt.Println(x2) // hello
var pointer *int // ポインタ型の宣言。初期値nil
fmt.Println(pointer) // nil
//fmt.Println(*pointer) // パニック(nilの参照先はNG)
ポインタを利用することでミュータブルになる
- 基本形や構造体は値渡しであるが、ポインタ値を渡すと、アドレスが渡されることになる。
- つまり、ポインタの参照先の値を変更すると、呼び出し先での変更が呼び出し元に反映される。
- なお、ポインタ渡しの場合もポインタ値のコピーを渡しているため、ポインタ値を変更しても呼び出し先の値は変わらない。
func main() {
val := 10
mutableFunc(&val) // ポインタ(アドレス)を渡す
fmt.Println("main",val) // 更新される→1000
}
func mutableFunc(pointer *int) {
*pointer = 1000 // ポインタ(アドレス)参照先の値を変更
fmt.Println("func",*pointer) // 1000
// 以下の場合は変更されない
//val := 1000
//pointer = &val // ポインタ(アドレス)を変更しているので元の値は変更されない。
}
マップとスライスの受け渡し
関数にマップやスライスを渡した場合、以下の動作となる。
- マップについては、構造体へのポインタのコピーを渡しているため、呼び出し元のマップも変更される。
- スライスについては、内容は変更できるが、サイズは変えられない。
- スライスが渡されると、「サイズ」、「キャパシティ」、「ポインタ」のコピーが作成される。
- スライス内の値を変える場合、ポインタの参照先の値が変わるため、呼び出し元へ反映される
- サイズとキャパシティの変更の場合は、呼び出し元のスライスは変更されない。
- この場合、新しいスライスが作成され、ポインタも別の値となる。
所感
- Goにおけるポインタの文法や、動作については理解できた
- しかし、その必要性についてはよくわからなかった。
- 元々、C言語にはあったが、難解かつ値の状態が追いづらくなるため、Java等で採用されなくなったと記憶している。
- なぜGo言語でポインタの概念が復活したのか。
- ガベージコレクションの効率化や、Json、インターフェース利用時に必要となるらしいので、後々理解できるか。。
- ポインタについては後の単元を学習した後、再度復習したほうがよさそう。