2021/05/04

Recent entries from same category

  1. Go に go:embed が入った。
  2. os/signal に NotifyContext が入った。
  3. 画像に再生マークつけるだけのコマンド作った。
  4. Go言語で Lisp 処理系を作った。
  5. Go 言語のスライス挿入ベンチマーク

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 by | Edit


blog comments powered by Disqus