Goでinvalid indirect of ... (type int64)が出た時に学ぶべき最低限のポインタ知識

thumnail
2020-07-20

背景

Goを使って開発しているときにinvalid indirect of hoge (type int64)というエラーに遭遇した。状況としては*int64型を引数に取るメソッドに対して、*を付与したint64型の値を渡したときに発生した。超簡単な例で紹介すると次のような状況であった。

package main

import (
    "fmt"
)

var id = int64(1)

func main() {
    foo(*id)
}

func foo(id *int64) {
    fmt.Println(id)
}

ここで「invalid indirect of id (type int64)」というエラーが吐かれた。この問題自体は調べて解決したのだが僕はポインタのことを理解していないことを確信した。ちょうど良い機会なので軽くおさらいして、この記事に備忘録として残しておくことにする。

int型とintのポインタ型

まず、int型という型を持つ値があるとする。

var p int

このpはint型である。ここで次のように定義するとintのポインタ型を定義できる。

var p *int

ポインタ型の正体は変数の値が格納されているアドレスを指す。初期値は値はnilが入る。なので次のようにintのポインタ型だけ定義して、出力するとnilが表示される。

var p *int
fmt.Println(p) // <nil>

では実際に値を代入してみようってことで次のようにint型の値を格納しようとするとコンパイルエラーが発生する。

var p *int
p = int(1) // cannot use int(1) (type int) as type *int in assignment
fmt.Println(p)

僕のようなにわかは、pの前に*を付ければ代入できるだろってことで付与してみたところpanicになった。

var p *int
*p = int(1) // panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1092eff]
fmt.Println(p)

またポインタ型を定義した段階でメモリ領域を確保していないので代入することができない。intのポイント型に対して、int型の値を代入したい場合は次のように変数を一度定義して、そのアドレスを渡せば良い。

var p *int
tmp := int(1)
p = &tmp
fmt.Println(p) // 0xc0000140f8

tmpの変数が保持しているアドレス(0xc0000140f8)がpに渡される。ポインタ型に*を付与するとアドレスに確保されている値を指すことができる。

var p *int
tmp := int(1)
p = &tmp
fmt.Println(*p) // 1

関数の引数がアドレス型を取る場合

今回のような引数がポインタ型を受け付ける関数に、引数を渡すためにはアドレスを渡す必要がある。int型のアドレスを指定するには&を付与してアドレスを渡せば良い。

package main

import (
    "fmt"
)

var id = int64(1)

func main() {
    foo(&id)
}

func foo(id *int64) {
    fmt.Println(id) // 0x115d230
}

ちなみにここでの注意点として、ポインタ型を引数に取る関数に対してアドレスを渡す場合は面倒かもしれないが上のように一度、変数を定義する必要がある。

KATUBLO

Copyright since 2018 KATUO All Rights Reserved.