Go言語:構造体とポインタ

Go言語:構造体 - sugilogのブログ のエントリーで、ポインタの部分の理解が曖昧だったので、復習。

参照したのは、こちら(M.Hiroi's Home Page / お気楽 Go 言語プログラミング入門)の記事

まずCのポインタのの利点

(1) 「参照呼び (call by reference) 」を実現する

(2) コンパクトで効率的なプログラムを書くことができる

Goでは、

  • Cとはできることが異なる。
  • ポインタについての整数値の代入によるアドレスの指定や演算はできない。
  • ポインタを利用した利点はそのままに、危険な操作ができないようになっている。

できることをまとめる

  • *のつく型は、その型のポインタ変数。
    • 初期値が指定されていない場合は、nilポインタに初期化される。(CのNULL相当)
  • &は、アドレス演算子。(Cの&と同様)
    • ポインタ変数の値を参照する場合は、変数のアクセスに*をつける。
    • ポインタ変数でない変数に*をつけてアクセスすると、エラーになる。
  • ポインタ変数に値を代入する場合も、変数のアクセスに*をつける。
    • ポインタに対して書き換えを行うので、同じポインタを参照する変数も書き換わる。
func main() {
  var n int  = 1
  var p *int = &n

  fmt.Println( n )
  fmt.Println( &n )
  fmt.Println( p )
  fmt.Println( *p ) 
}

//=>
// 1
// 0x10328000
// 0x10328000
// 1
func main() {
  var n int  = 1
  fmt.Println( *n )
}

//=>
// invalid indirect of n (type int)
//  [process exited with non-zero status]
func main() {
  var n int  = 1
  var p *int = &n

  fmt.Println( n )
  fmt.Println( &n )
  fmt.Println( p )
  fmt.Println( *p ) 

  *p = 2

  fmt.Println( n )
  fmt.Println( &n )
  fmt.Println( p )
  fmt.Println( *p ) 
}

//=>
// 1
// 0x10328000
// 0x10328000
// 1
// 2
// 0x10328000
// 0x10328000
// 2

構造体とポインタ

参照した記事では、配列で説明しているけど、今は構造体に置き換えていく。

アドレス演算子付きで構造体を初期化。(patternA)

  • 出力結果で、1つ目は&が付いているので、ポインタであることを示している。
  • 2つ目は、*をつけてアクセスしたので、値を示している。

ポインタを渡して、別の変数でフィールドの書き換えを行う。(patternB)

  • (話の流れ的に当たり前だけど)ポインタを渡した元の変数のフィールドも、同様にして書き換えられる。
    • 説明的には変数のフィールドが書き換わるというのはおかしいな。参照している先が変わったというべきか。
  • だってポインタだもの。

値を渡して、別の変数でフィールドの書き換えを行う。(patternC)

  • 値を渡した元の変数に変化はない。
  • だって、ポインタが違うもの。

new関数でゼロ初期化。(patternD、patternE)

  • アドレス演算子をつけて初期化したパターンと同じ挙動をする。
  • 値を代入するパターンも同様。

ポインタと構造体