A Tour of Goのざっくりまとめ③ <More types: pointers, structs編>
こちらの記事の続きです。
mattsun-plapla.hatenablog.com
mattsun-plapla.hatenablog.com
今回のMore Types編は長いので分割して書いていきます。
Pointers
ポインタは値のメモリアドレスを指す。
メモリには1バイトごとに番号が振られている。
例えばint型の100を指すSomething変数の場合、
&SomethingとすることでSomething変数のアドレスへアクセスできる。
このアドレスを値として受け取るのがポインタ型であり、
Pointer変数であれば、*Pointerと、先頭にアスタリスクをつける。
func main() { a := 42 b := a fmt.Printf("%T %v\n", a, a) // int 42 fmt.Printf("%T %v\n", b, b) // int 42 c := 42 d := &c fmt.Printf("%T %v\n", c, c) // int 42 fmt.Printf("%T %v\n", c, c) // *int 0xc000014108% アドレス }
アドレスをもつポインタ型を通して、直接そのアドレスが示す値(変数)にアクセスできる。
*d = 500 fmt.Printf("%T %v\n", c, c) // int 500 // cのアドレスを格納するポインター型dのポインタを通して変数cの値に直接アクセスした fmt.Printf("%T %v\n", *d, *d) // int 500 // ポインタ型dには500が格納されている
C言語などはやっていないので、正直ポインタやアドレスは馴染みがなくまだまだ学習する必要があると思います...
Struct
...(Structのページ、解説が少なすぎないか...orz)
Structは他プログラム言語でいうclassと比較して解説されることが多い。
主な理由として、OOPのような表現ができるからだと考えられる。
package main import "fmt" // Vertexというstruct typeを定義 type Vertex struct { X, Y int } func main() { v := Vertex{3, 19} fmt.Println(v) // {3, 19} // Struct typeの与えられたvからX, Yを取り出すイメージ(OOP的) fmt.Println(v.X, v.Y) // 3 19 }
Struct内では異なる型で、異なる値(Field or Propertyと呼ばれる)を入れることができる。
異なるFieldからつくられた集合体(Scheme)を定義する際にStructは使われる。
SchemeとClassはほぼ同義で、SchemeからObjectをつくることができるというイメージ。
Struct Typeは構造体を示すSchemeでしかないため、枠組みや設計図のようなイメージ。
上述コードの例だと、変数vはVertexというStruct型をもつStructであり、Vertexはint型のX, Yというフィールドを持っている構造体という感じ。
ちなみにフィールドの名前は小文字だと外部からアクセス(参照)できない。
また、下記のように構造体内で示されているフィールドに値を与えない場合、初期値が設定されている。
(Int型は0, bool型はfalse, string型は空白)
type Vertex struct { X int T bool S string } func main() { var v Vertex fmt.Println(v) // {0 false } }
Anonymous struct
上述の例だと、Vertexを定義する際にvという変数に一旦渡していたが、定義と値渡しを同時に行うAnonymous structというやり方もある。
ただし下記のケースでいうと、eを再活用することはできない。eというStructを再利用しない場合には便利な方法でもある。
package main import "fmt" func main() { e := struct { FirstName, LastName string Phone int Address string }{ FirstName: "Sarah", LastName: "Conner", Phone: 1234567, Address: "Earth", } fmt.Println(e) // {Sarah Conner 1234567 Earth} fmt.Println(e.address) // Earth }
メソッド
Struct型を引数にとることで、メソッドをつかうこともできる。
type Vertex struct { X, Y int } // 引数であるStruct内フィールドX,Yを足す関数Sum func (v Vertex) Sum() int { return v.X + v.Y } func main() { v := Vertex{10, 20} fmt.Println(v.Sum()) // 30 }
Pointers to Struct
structのフィールドは、structのポインタを通してアクセスすることも可能。
Structにポインタを渡す書き方は下記の通り。
func main() { p := People{"Tom", 38} fmt.Println(p) // {Tom 38} pp := &p // Struct ppにポインタを渡す (*pp).Name = "Mike" fmt.Println(p) // {Mike 38} }
これでpのアドレスをppに渡し、ppポインタを通してName変数の実体を書き換えている。
値渡しでなく参照渡しのため、pp=pのコピーになっていない? (理解が間違っていたらすみません
上記の例だと、ポインタppのName変数の実体を指定する際に、(*pp).Nameと書いている。
これは、(*pp).Nameと*(pp.Name) を混同しないためらしいが、括弧はなくてもいいらしい。
// 別の例 type Vertex struct { X, Y int } func Vertex1000(v Vertex) { v.X = 1000 } func Vertex1000_2(v *Vertex) { v.X = 1000 } func main() { v := Vertex{1, 2} Vertex1000(v) fmt.Println(v) // {1, 2} (値は変わらない) v2 := &Vertex{1, 2} Vertex1000_2(v2) fmt.Println(v2) // &{1000, 2} (値が変わる) }
Anonymous fields
Struct型にあるフィールドの名前は必ずしも定義しなくても良い。
一部のフィールドだけ定義されていない場合でもOK
データ型が記述してあれば、それが同時にフィールド名にもなる。
type Anonymous struct { int bool string } func main() { a := Anonymous{4, true, "OK"} fmt.Println(a) // {4 true OK} }
Nested Struct
Struct型にあるフィールドはどんなデータ型でもいいわけだから、当然Struct型でも良い。
これをNested Structという。Nested StructでAnonymous fieldsを定義しても良い。
type Job struct { companyName string salary int } type Family struct { Papa string Job Job // struct型を定義 (Nested Struct) Mama string Son string } func main() { family := Family{ Papa: "Tom", Mama: "Jessica", Son: "Mike", Job: Job{"A company", 300}, } fmt.Println(family) // {Tom {A company 300} Jesica Mike} fmt.Printf("Papa's salary is %v", family.Job.salary) // 300 }
Struct Literals
Structを列挙できる。
type Job struct { X, Y int } var ( j1 = Job{1, 2} j2 = Job{3, 4} j3 = &Job{5, 6} j4 = Job{} ) func main() { fmt.Println(j1, j2, j3, j4) }