Go言語を始めて喜んだことの一つにポインタがあります。一瞬、ポインタがあれば「やりたい放題」と思ったのですが違いました。Goのポインタはほぼ安全。Cのように不作法な操作は許してくれないことが分かりました。でもねえ、やっぱり「危ない」ポインタ操作をやりたい人はいたみたい。その名もunsafeモジュール。
※「やっつけな日常」投稿順 indexはこちら
※今回実験はAndroidスマホ(Termux環境)上のGoを使って行っています。アドレスサイズも64ビット、int型も64ビット。
別にちゃんとしたOSの走っているGo環境で危ないポインタ操作をしたいと思ったわけじゃないのです。マイコン環境でGoするときにポインタさえあれば、結構なんでもできるじゃん、と。実際C言語でマイコン用にコードを書くとき、SDKが周辺装置をサポートしてなくても、ポインタとデータシートがあればなんとかなります、大抵は。
しかし、Goのポインタを学んでみると結構安全であることに納得、つまり
Goのポインタには演算が許されていない(普通は)
Cのようにポインタにオフセットを足して次のIOレジスタ操作するとかメモリをアクセスするということができません。それどころかメモリを指すポインタ型の変数に即値を代入するようなことも許されてないようです。あちゃ~ ちゃんと危ないところは塞いであるのね。。。
そう思っていたら違いました。unsafeというパッケージを発見。ドキュメントが以下に。
これを使うと以下ができます。
-
- ポインタを演算可能な型に変換できる
- 演算した後でポインタに戻せる(チョイとメンドイけれども)
つまり、ポインタの演算もアドレス代入も思うがまま?ホントか?
そこで書いてみた実験用コードが以下に。
package main import ( "fmt" "unsafe" ) func main() { mx := [8] int{1,2,3,4,5,6,7,8} var iptr *int iptr = &mx[0] fmt.Println(*iptr) fmt.Println(iptr) x := unsafe.Pointer( uintptr(unsafe.Pointer(iptr)) + unsafe.Sizeof(mx[0]) ) y := (*int)(x) fmt.Println(x) fmt.Println(y) fmt.Println(*y) }
上記の iptr というポインタは、Goのお作法どおりの使い方で危ないことはしておりませんな。
しかし、続く、x, y の方は危ない使い方(unsafe モジュール使用)です。メンドイのは、通常のポインタを一度危ないポインタ型 unsafe.Pointerに変換してから、演算可能な型 uintptr にしないとならない点。また、unsafe.Pointerのままでは通常型の値を参照することができずGoには珍しい型キャスト的なことをしないとならないらしい点。でもま、ポインタ相手に足し算して、そのポインタが指している変数の値を読み出せておりますぞ(冒頭のアイキャッチ画像参照。)
なお、Goのfmt.Println()にポインタ型の変数を渡すと何もしなくても16進で表示してくれます。便利。
Cでポインタが使えるといってもそれほど複雑な事をするわけでないです、私は。よって、Goでこの程度出来れば十分かな~。でもまあ、こういうポインタの使い方をすると、移植性が悪くなるとか、抽象度が下がるとか張り倒されそうな気もしますが、マイコンだよ、マイコン。即ハードの低レベル。。。
ああ、でもマイコン用のTinyGoでも unsafe 使えるのだよね?大丈夫か?後で確かめないと。