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に組み込むことも可能。