Golangハマり備忘録 - structの代入、structのポインタ、ポインタの配列/スライス

値渡しの代入

golangの値のコピーはClangの代入演算子とほぼ同じ。 structの代入はstruct全内容のコピーをとる。(値渡し)

type Sample struct {
  Hoge int
  Huga string
}

var val, val2 Sample     // val == 0x4000、val2 == 0x5000のアドレスがそれぞれ割り当てられたとする
val = Sample{10, "test"} // 0x4000 == {10, "test"}
val2 = val               // 0x4000 == {10, "test"}、0x5000 == {10, "test"}
val2.Hoge = 20           // 0x4000 == {10, "test"}、0x5000 == {20, "test"}

ポインタ

ポインタとは、変数のショートカットを作る方法です。 - 苦しんで覚えるC言語 15章:ポインタ変数の仕組み

  • 変数はメモリに物理配置
  • そのメモリの番地を変数にもたせて間接参照

ハマり:ポインタの配列にローカル変数から何かしらつっこむとき

ローカル変数の&を単純にとると、単純にローカル変数に割り当てられたアドレスをコピって値にする。

type SomeType struct {
  X int
}

func main() {
type SomeType struct {
  X int
}

func main() {
  pary := []*SomeType

  var v SomeType           // メモリアドレス 0x40000000で確保されたとする

  v = SomeType{1}          // 0x40000000 == {1}
  pary = append(pary, &v); // pary == [0x40000000]

  v = SomeType{2}          // 0x40000000 == {2} に上書き 
  pary = append(pary, &v); // pary == [0x40000000, 0x40000000]

  for _, pval := range pary {
    fmt.Println(*pval);
  }
  // {2}
  // {2}
  // と表示。
}

paryに別個にallocateされた領域を食わせる場合、newって明示的に領域をアロケートする。

type SomeType struct {
  X int
}

func main() {
  pary := []*SomeType

  var v *SomeType         // メモリアドレス 0x40000000で確保されたとする
  v = new SomeType{}      // 0x40000000 == 0x50000000
  v.X = 1
  pary = append(pary, v); //  pary == [0x50000000]

  v = &SomeType{2}        // 0x40000000 == 0x50000008、↑と同様のシンタクスシュガー 
  pary = append(pary, v); //  pary == [0x50000000, 0x50000008]

  for _, pval := range pary {
    fmt.Println(*pval);
  }
  // {1}
  // {2}
  // と表示。
}

Javaの参照型・string型の代入は全部アロケート相当なのでそっち慣れてるとハマる。

ローカル変数のポインタを関数でreturnしてもつかえるのも理解するうえでいやらしい。。CやC++とは違い、ローカル変数のアドレスをスタックから分離して全てガベコレできるようにしている。「コールスタック解放≠コールスタック内の局所変数の領域解放」 Javaの実装と同じ要領で、golangの使用感はCテイストだがコールスタックの処理はCでいうポインタ渡しでうまいことしているんだろうか