はじめまして。まいたけ(@maitaketurn)です。
普段は都内のベンチャーでGo,Pythonなど書いています。
golang.tokyoに参加してきました!(初勉強会参加でした)
自分の復習も兼ねてレポってみます〜
絶対に分かるポインタ / tenntennさん
-
ポインタとは
- 変数の格納先を表す値:破壊的な操作をする際に使用する
- ポインタのポインタもある:**Tなら*Tのポインタのこと。
-
型の表記方法
- 名前のついた型:組み込み型など
- 型リテラル:識別子(名前)がついていない型。スライス、構造体など。型リテラルのポインタ型も定義できる。
-
ポインタ演算
- &:ポインタ値を取得する
- *:ポインタが指す先を取得する
これだけ!シンプル!!
例)
package main
import (
"fmt"
)
func main() {
type T *****************int
var t ***T
fmt.Println(&t)
}
出力結果
0x40c128
tのアドレスが出力される。*の数は関係ない
ポインタはなぜ必要?
代入だとコピーになる。
p := struct {
age int
name string
}{age: 10, name: "Gopher"}
p2 := p // コピー
p2.age = 20
println(p.age, p.name)
println(p2.age, p2.name)
出力結果
10 Gopher
20 Gopher
p2にの値を変更したとしても、pの値は変更されない。
ポインタが絡んでくる話
- 内部でポインタを使っているデータ型
- スライス
- マップ
- チャネル
func main() {
ns := []int{10, 20, 30}
ns2 := ns
ns[1] = 200
println(ns[0], ns[1], ns[2])
println(ns2[0], ns2[1], ns2[2])
}
出力結果
10 200 30
10 200 30
ポインタを使っているので、nsの変更はns2にも反映される。破壊的な操作。
スライスについて
構造体で定義されている。以下をフィールドとして持つ
-
スライス元となる配列のポインタ
-
length
-
capacity
-
appendの挙動
- capacityが足りる場合:新しい要素をコピーしてlengthを更新する
- capacityが足りない場合:元のcapacityの2倍程度の領域を新たに確保する
package main
import "fmt"
func f(ns []int){
ns[1] = 200
ns = append(ns,40,50)
}
func main() {
ns := []int{10, 20, 30}
f(ns)
fmt.Println(ns)
}
出力結果
[10 200 30]
appendした際にcapacityが足りないので、新しくアドレスが振られるため。
appendしたら戻り値を新しい代入すれば40,50を追加できる。
package main
import "fmt"
func f(ns []int) []int{
ns[1] = 200
ns = append(ns,40,50)
return ns
}
func main() {
ns := []int{10, 20, 30}
ns2 := f(ns)
fmt.Println(ns)
fmt.Println(ns2)
}
出力結果
[10 200 30]
[10 200 30 40 50]
参考
https://speakerdeck.com/kaznishi/180713-lt
https://qiita.com/hitode7456/items/562527069e13347b89c8
まとめ
ポインタの根幹の考え方自体はシンプル。
スライスやマップなども内部にポインタを持っているので、ポインタが分かればそれらも分かる!(はず)
まだお一人目の発表分しか書いてないのですが、長くなってしまったので他の発表者の方の内容については別記事にて!