...

パッケージ unsafe

import "unsafe"
概要
目次

概要 ▾

unsafe パッケージは,Go プログラムの型安全を回避する操作を含みます。

unsafe をインポートするパッケージは移植性がなく, Go 1 互換性ガイドラインによって保護されていません。

func Alignof

func Alignof(x ArbitraryType) uintptr

Alignof は任意の型の式 x を取り, v が var v = x を介して宣言されているかのように,仮想変数 v に必要な配置を返します。 これは, v のアドレスが常に 0 mod m になるような最大値 m です。 reflect.TypeOf(x).Align() が返す値と同じです。 特別な場合として,変数 s が構造体型であり, f がその構造体内のフィールドである場合, Alignof(s.f) は構造体内のその型のフィールドの必要な配置を返します。 この場合は reflect.TypeOf(s.f).FieldAlign() によって返される値と同じです。 Alignof の戻り値は Go 定数です。

func Offsetof

func Offsetof(x ArbitraryType) uintptr

Offsetof は, x で表されるフィールドの構造体内のオフセットを返します。 これは, structValue.field の形式でなければなりません。 つまり,構造体の先頭からフィールドの先頭までのバイト数を返します。 Offsetof の戻り値は Go 定数です。

func Sizeof

func Sizeof(x ArbitraryType) uintptr

Sizeof は任意の型の式 x を取り, v が var v = x を介して宣言されているかのように,仮想変数 v のサイズをバイト数で返します。 サイズには, x によって参照される可能性のあるメモリは含まれません。 たとえば, x がスライスの場合, Sizeof はスライスが参照するメモリのサイズではなく,スライス記述子のサイズを返します。 Sizeof の戻り値は Go 定数です。

type ArbitraryType

ArbitraryType は文書化のみを目的としており,実際には unsafe パッケージの一部ではありません。 任意の Go 式の型を表します。

type ArbitraryType int

type Pointer

Pointer は,任意の型へのポインタを表します。 ポインタ型には,他の型には使用できない 4 つの特別な操作があります。

- 任意の型のポインタ値は Pointer に変換できます。
- Pointer は任意の型のポインタ値に変換できます。
- uintptr は Pointer に変換できます。
- Pointer は, uintptr に変換できます。

したがって,Pointer を使用すると,プログラムは型システムを無効にして任意のメモリを読み書きできます。 細心の注意を払って使用する必要があります。

以下の Pointer を含むパターンが有効です。 これらのパターンを使用しないコードは,今日は無効になる可能性があり,将来的には無効になる可能性があります。 以下の有効なパターンでさえも重要な警告があります。

"go vet" を実行すると,これらのパターンに適合しない Pointer の使用法を見つけるのに役立ちますが, "go vet" からの沈黙はコードが有効であるという保証ではありません。

(1) a *T1 から *T2 への Pointer への変換。

T2 が T1 より大きくなく,そして2つが同等のメモリレイアウトを共有するという条件で,この変換は1つの型のデータを別の型のデータとして再解釈することを可能にする。 例は math.Float64bits の実装です。

func Float64bits(f float64) uint64 {
	return *(*uint64)(unsafe.Pointer(&f))
}

(2) Pointer から uintptr への変換 (ただし,Pointer への変換は行わない) 。

ポインタを uintptr に変換すると,ポイントされた値のメモリアドレスが整数として生成されます。 そのような uintptr の通常の使い方はそれを表示することです。

uintptr から Pointer への変換は一般に無効です。

uintptr は整数であり,参照ではありません。 Pointer を uintptr に変換すると,ポインタの意味を持たない整数値が作成されます。 たとえ uintptr があるオブジェクトのアドレスを保持していても,オブジェクトが移動してもガベージコレクタはその uintptr の値を更新しません。 また,その uintptr はそのオブジェクトが回収されないようにしません。

残りのパターンは, uintptr から Pointer への唯一の有効な変換を列挙します。

(3) 算術演算子を使ったポインタから uintptr への変換およびその逆変換。

p が割り当てられたオブジェクトを指している場合は, uintptr への変換,オフセットの追加,および Pointer への変換の逆変換によって,オブジェクトを進めることができます。

p = unsafe.Pointer(uintptr(p) + offset)

このパターンの最も一般的な使い方は,構造体のフィールドや配列の要素にアクセスすることです。

// f := unsafe.Pointer(&s.f) と同じです。
f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))

// e := unsafe.Pointer(&x[i]) と同じです。
e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

このようにポインタにオフセットを加算したり減算したりすることは有効です。 通常は位置合わせのために,ポインタを丸めるために &^ を使用することも有効です。 すべての場合において,結果は元の割り当てられたオブジェクトを指し続ける必要があります。

C とは異なり,元の割り当ての終わりを超えてポインタを進めることは無効です。

// INVALID: 割り当てられたスペースの外側の端点。
var s thing
end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s))

// INVALID: 割り当てられたスペースの外側の端点。
b := make([]byte, n)
end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n))

両方の変換は,それらの間に介在する算術のみを含めて,同じ式に現れなければならないことに注意してください。

// INVALID: uintptr を Pointer に戻す前に変数に格納することはできません。
u := uintptr(p)
p = unsafe.Pointer(u + offset)

ポインタは割り当てられたオブジェクトを指していなければならないので, nil ではないかもしれないことに注意してください。

// INVALID:nil ポインタの変換
u := unsafe.Pointer(nil)
p := unsafe.Pointer(uintptr(u) + offset)

(4) syscall.Syscall を呼び出すときの Pointer から uintptr への変換

パッケージ syscall の Syscall 関数は,それらの uintptr 引数を直接オペレーティングシステムに渡します。 オペレーティングシステムは,呼び出しの詳細に応じて,それらのいくつかをポインタとして再解釈する場合があります。 つまり,システムコールの実装は,暗黙的に特定の引数を uintptr からポインタに変換しています。

ポインタ引数を引数として使用するために uintptr に変換する必要がある場合は,その変換を呼び出し式自体に含める必要があります。

syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))

コンパイラは,参照された割り当てオブジェクトが存在する場合は保持され,呼び出しが完了するまで移動されないようにすることで,アセンブリで実装された関数の呼び出しの引数リスト内の uintptr に変換された Pointer を処理します。 呼び出し中にオブジェクトは不要になったようです。

コンパイラがこのパターンを認識するためには,変換が引数リストに含まれている必要があります。

// INVALID: システムコール中に uintptr を暗黙的に Pointer に戻す前に,変数に格納することはできません。
u := uintptr(unsafe.Pointer(p))
syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))

(5) reflect.Value.Pointer または reflect.Value.UnsafeAddr の結果を uintptr から Pointer に変換する。

Pointer および UnsafeAddr という名前のパッケージ reflect の Value メソッドは,最初に "unsafe" をインポートせずに呼び出し側が結果を任意の型に変更できないようにするために, unsafe.Pointer の代わりに uintptr 型を返します。 ただし,これは結果が壊れやすく,呼び出しを行った直後に同じ式で Pointer に変換する必要があることを意味します。

p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))

上記の場合と同様に,変換前に結果を保存することは無効です。

// INVALID: uintptr を Pointer に戻す前に変数に格納することはできません。
u := reflect.ValueOf(new(int)).Pointer()
p := (*int)(unsafe.Pointer(u))

(6) reflect.SliceHeader または reflect.StringHeader データフィールドと Pointer の間の変換。

前の場合と同様に,リフレクトデータ構造体 SliceHeader と StringHeader は,呼び出し側が最初に " 安全でない " をインポートせずに結果を任意の型に変更できないようにするために,フィールド Data を uintptr として宣言します。 しかし,これは SliceHeader と StringHeader が実際のスライスまたは文字列値の内容を解釈するときにのみ有効であることを意味します。

var の文字列
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // ケース 1
hdr.Data = uintptr(unsafe.Pointer(p))              // ケース 6 (このケース)
hdr.Len = n

この使用法では, hdr.Data は実際には文字列ヘッダー内の内部のポインターを参照するための代替手段であり, uintptr 変数自体ではありません。

一般的に, reflect.SliceHeader と reflect.StringHeader は,実際のスライスや文字列を指す *reflect.SliceHeader と *reflect.StringHeader としてのみ使用されるべきで,プレーンな構造体としては使用されません。 プログラムは,これらの構造体型の変数を宣言または割り当ててはいけません。

// INVALID: 直接宣言されたヘッダは参照として Data を保持しません。
var hdr reflect.StringHeader
hdr.Data = uintptr(unsafe.Pointer(p))
hdr.Len = n
s := *(*string)(unsafe.Pointer(&hdr)) // p はすでに失われているかもしれない
type Pointer *ArbitraryType