2021/05/04

Go ではポインタは扱えるけれど、ポインタ演算は扱えないというのが共通認識でした。もちろん unsafe.Pointeruintptr を使う事で、出来なくはなかったのですが簡単ではありませんでした。

package main

import (
    "unsafe"
)

type foo struct {
    k int64
    v int64
}

func main() {
    f := &foo{3,4}

    // unsafe.Pointer() で匿名ポインタにして
    // uintptr() で演算可能にして
    // +8 バイト(64bit)足して
    // unsafe.Pointer で匿名ポインタに戻して
    // そこにはフィールド v があるはずなので *int64 にキャストして
    // デリファレンスすれば出来上がり
    *(*int64)(unsafe.Pointer((uintptr(unsafe.Pointer(f))+8))) = 5 // グヒヒ

    println(f.v) // 5
}

Go 1.17 からは unsafe.Addunsafe.Slice が導入される事で、少しだけ簡単にポインタ演算そしてポインタからスライスを復元する事ができる様になります。(参照)

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    b := []byte{12345}
    pb := &b[3]
    fmt.Println(unsafe.Slice(pb, 2)) // 4, 5

    // sizeof(byte) 分ポインタをずらす
    pb = (*byte)(unsafe.Add(unsafe.Pointer(pb), 1))
    fmt.Println(*pb) // 5

    v := []int{23456}
    pi := &v[3]
    // オフセット3からのスライスを得る
    fmt.Println(unsafe.Slice(pi, 2)) // 5, 6

    // sizeof(int) 分ポインタをずらす
    pi = (*int)(unsafe.Add(unsafe.Pointer(pi), 8))
    fmt.Println(*pi) // 6
}

もちろん不正なオフセットを指定して unsafe.Add を呼び出してデリファレンスしたり、不正なサイズを指定してスライスを復元したりすると panic する事になります。

Posted at 00:25 | WriteBacks () | Edit
Edit this entry...

wikieditish message: Ready to edit this entry.






















A quick preview will be rendered here when you click "Preview" button.