LoginSignup
23
4

More than 3 years have passed since last update.

Go言語: ポインターとそれに関する型(uintptr, unsafe.Pointer)

Last updated at Posted at 2020-04-12

この記事について

Go言語の以下のポインター関連の型について簡単に整理します。

  • & で取得するアドレス
  • uintptr
  • unsafe.Pointer

& で取得するアドレス

  • 変数のアドレスを表す整数値
  • 値は整数値だが、型はそれが指す型に応じて異なる
    i := 1
    s := []string{"x", "y"}

    pi := &i    // pi の型は *int
    ps := &s    // ps の型は *[]string
  • 当然ながらアドレスを参照できる → *piとする
  • しかし、C言語のようにポインターの演算はできない → pi++ とか出来ない

uintptr

  • Go言語の Builtin 型であり、アドレスを格納できる大きさを持つ「整数型」
  • 単なる整数型なので、アドレスを参照することはできない → *pi が出来ない
  • 単なる整数型なので、演算ができる → pi++ とかが出来る。
    • ただしC言語とは異なり単なる整数演算になる。pi++ は値に1を足すだけで、次のオブジェクトを指すアドレスにインクリメントするのではない
  • uintptr がオブジェクトのアドレスを指していても、そのオブジェクトは GC で回収されうる
    • GC は、uintptr をオブジェクトを参照してるものと見なさないため
    • 後述の注意事項も参照

unsafe.Pointer

  • すべての型のポインターを表せる型。C言語の void * という感じ
  • 以下の特性を持つ
    • 任意の型のポインター から unsafe.Pointerへ変換可能。またその逆も可能。
    • uintptr から unsafe.Pointerへ変換可能。またその逆も可能。
  • Go の型管理の機構を迂回して任意のアドレスを読み書きてしまうため、危険であり通常使うことはない

注意

  • uintptr と unsafe.Pointer の変換の間に GC が走る余地を入れないこと
これは大丈夫
    p = unsafe.Pointer(uintptr(p) + offset)
これは危険
    // 1行目と2行目の間に、p が GC で回収されるかも。
    u := uintptr(p)
    p = unsafe.Pointer(u + offset)

サンプル

実用性は無いが、int32 のスライスを C言語のように処理して合計値を求める例。

package main

import (
    "fmt"
    "unsafe"
)

func sum(ptr *int32, size int) int32 {
    var sum int32
    p := unsafe.Pointer(ptr)

    for i := 0; i < size; i++ {
        // 4バイト(32ビット)、ポインターを進める
        sum += *(*int32)(unsafe.Pointer(uintptr(p) + uintptr(i*4)))
    }
    return sum
}

func main() {
    s := []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    // sum には、スライスのアドレス &s ではなく、スライスが指してる配列の
    // 先頭アドレス &s[0] を渡す必要あり
    fmt.Printf("Sum = %d\n", sum(&s[0], len(s)))
}

実行結果: Sum = 55

参考

23
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
4