Goの勉強をやり始めたのでメモ。
「プログラミング言語 Go」を読んでる。
- Go言語は安全性についての特性を多く持っている。
- 文字列の減算はできない。
- スライスなどの組み込み型の内部への直接的なアクセスを防ぐ。
- 関数の機械語のコードにアクセスできない。
- ゴルーチンがどのOSスレッドで実行されているか知ることができない。
- 変数の実際のアドレスが変わっても、ポインタで同じようにアクセスできる。
- 背後にある詳細を隠蔽することで、Goのプログラムは移植性が高い。
- ただし、最高の性能を達成したいなどの理由により、これらの安全性を犠牲にしたい場合がある。
- unsafe パッケージは通常のプログラムではほぼ使われない。
unsafe.Sizeof、Alignof, Offsetof
unsafe.Sizeof
はバイト数の大きさを返す。- 例えば
float64
は 8 バイト、文字列は 16 バイト(2ワード)
- 例えば
- コンピュータはこれらの値が適切に整列されているときに最も効率的にメモリへ読み書きできる。
- つまり、
float64
のアドレスは8の倍数であるべき。 - 合成型の値の大きさは、穴があるかもしれないので、フィールドの大きさの合算よりも大きくなる可能性がある。
- つまり、
fmt.Println(unsafe.Sizeof(struct { bool float64 int16 }{})) // 24(3ワード) fmt.Println(unsafe.Sizeof(struct { float64 int16 bool }{})) // 16(2ワード) fmt.Println(unsafe.Sizeof(struct { bool int16 float64 }{})) // 16(2ワード)
unsafe.Alignof
は必要な整列を返す。- おそらく、つまり、先頭のアドレスが何バイトの倍数であるべきか。基本的に1ワードよりも大きく整列されることはないので、8バイトより大きくなることはない?
unsafe.Offsetof
はフィールドに対して構造体の先頭からのオフセットを穴も考慮して返す。- これらの関数は名前にも関わらず安全である。
unsafe.Pointer
unsafe.Pointer
型は、いかなる変数のアドレスでも保持できる特殊なポインタである。- 通常のポインタから
unsafe.Pointer
を通して値を参照することはできない。(型が不明なため) - 比較可能であり、ゼロ値は
nil
である。
- 通常のポインタから
- 普通の
*T
をunsafe.Pointer
に変換することができる。 unsafe.Pointer
を*T
に変換することもできる。- 結果として任意の値をメモリに書き込むことができるため、型システムを破壊する。
unsafe.Pointer
をuintptr
に変換することができる。uintptr
をunsafe.Pointer
に変換することもできる。- 同様に型システムを破壊する。
- 多くの
unsafe.Pointer
の値は、普通のポインタの実際の数値的なアドレスへの変換とその逆変換に関する仲介を果たす。 - 以下のコードは引越しさせるGCの考慮が抜けているため、正しく動作しない可能性がある。
- 2行目に到達するタイミングで変数
x
のアドレスは変わっている可能性があるため。
- 2行目に到達するタイミングで変数
tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b) pb := (*int16)(unsafe.Pointer(tmp)) *pb = 42
- 以下のコードは誤りである。
-
new
で生成した変数はどこからも参照されておらず、すぐにガベージコレクタにより回収されるため。
-
pT := uintptr(unsafe.Pointer(new(T)))
- よって、
uintptr
の値はすべて「以前のアドレスを含んでいる」とし、unsafe.Pointer
からuintptr
への変換やuintptr
を使う操作の回数を最低限にするべきである。- 可能であれば、1つの式の中に含めたほうが良い。
- reflect パッケージなどの
uintptr
を返すメソッドを使用する場合は、なるべくすぐにunsafe.Pointer
型に変換するべきである。
cgo を使った C のコードの呼び出し
- 多くのパッケージが、その実装の言語に関係なく C と互換性がある API を公開している。
- cgoは
import "C"
の前に書いたコメントにより、コンパイラのオプションを指定できる。#include
など。#cgo
を使うことで、Cのツールチェーンに対する追加のオプションを指定できる。
C.bz_stream
などのようにC.
を付けることで、Cの API を使用できる。C.uint
とuint
は同じ幅だが、型としては区別される。- Cに渡すポインタは基本的に
unsafe.Pointer
を使用する。 - CのライブラリをGoに組み込むだけでなく、GoのライブラリをCに組み込むことも可能。