Package unsafe

import "unsafe"
Overview
Index

Overview ▾

不安全的软件包包含绕过Go程序的类型安全的操作.

导入不安全的软件包可能是不可移植的,并且不受Go 1兼容性准则的保护.

func Alignof

func Alignof(x ArbitraryType) uintptr

Alignof takes an expression x of any type and returns the required alignment of a hypothetical variable v as if v was declared via var v = x. It is the largest value m such that the address of v is always zero mod m. It is the same as the value returned by reflect.TypeOf(x).Align(). As a special case, if a variable s is of struct type and f is a field within that struct, then Alignof(s.f) will return the required alignment of a field of that type within a struct. This case is the same as the value returned by reflect.TypeOf(s.f).FieldAlign(). The return value of Alignof is a Go constant.

func Offsetof

func Offsetof(x ArbitraryType) uintptr

Offsetof返回x表示的字段的结构内的偏移量,该偏移量的格式必须为structValue.field. 换句话说,它返回结构开始与字段开始之间的字节数. Offsetof的返回值为Go常数.

func Sizeof

func Sizeof(x ArbitraryType) uintptr

Sizeof接受任何类型的表达式x并返回假设变量v的字节大小,就好像v是通过var v = x声明的一样. 该大小不包括x可能引用的任何内存. 例如,如果x是切片,则Sizeof返回切片描述符的大小,而不是该切片所引用的内存的大小. Sizeof的返回值是Go常数.

type ArbitraryType

ArbitraryType此处仅出于文档目的,实际上并不属于不安全软件包. 它表示任意Go表达式的类型.

type ArbitraryType int

type Pointer

指针表示指向任意类型的指针. 指针类型有四种特殊操作,而其他类型则没有:

- A pointer value of any type can be converted to a Pointer.
- A Pointer can be converted to a pointer value of any type.
- A uintptr can be converted to a Pointer.
- A Pointer can be converted to a uintptr.

因此,指针允许程序击败类型系统并读写任意内存. 使用时应格外小心.

以下涉及Pointer的模式是有效的. 不使用这些模式的代码今天可能无效,或者将来可能无效. 甚至下面的有效模式也带有重要的警告.

运行" go vet"可以帮助查找不符合这些模式的Pointer用法,但是对" go vet"的沉默不能保证该代码有效.

(1)将* T1转换为指向* T2的指针.

假设T2不大于T1,并且两个共享相同的内存布局,则此转换允许将一种类型的数据重新解释为另一种类型的数据. 一个示例是math.Float64bits的实现:

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

(2)将Pointer转换为uintptr(但不返回给Pointer).

将Pointer转换为uintptr会生成所指向的值的内存地址(整数). 这种uintptr的通常用法是打印它.

通常,将uintptr转换回Pointer无效.

uintptr是整数,不是引用. 将Pointer转换为uintptr会创建一个没有指针语义的整数值. 即使uintptr拥有某个对象的地址,垃圾回收器也不会在对象移动时更新该uintptr的值,该uintptr也不会使该对象被回收.

其余模式列举了从uintptr到Pointer的唯一有效转换.

(3)用算术将指针转换为uintptr并返回.

如果p指向已分配的对象,则可以通过转换为uintptr,添加偏移量并将其转换回Pointer的方式将其推进对象.

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

此模式最常见的用法是访问数组的结构或元素中的字段:

// equivalent to f := unsafe.Pointer(&s.f)
f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))

// equivalent to e := unsafe.Pointer(&x[i])
e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

以这种方式从指针添加和减去偏移量都是有效的. 通常使用&^舍入指针(通常用于对齐)也是有效的. 在所有情况下,结果都必须继续指向原始分配的对象.

与C语言不同,将指针恰好超出其原始分配的末尾是无效的:

// INVALID: end points outside allocated space.
var s thing
end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s))

// INVALID: end points outside allocated space.
b := make([]byte, n)
end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n))

请注意,两个转换必须出现在相同的表达式中,并且它们之间只有中间的算术运算:

// INVALID: uintptr cannot be stored in variable
// before conversion back to Pointer.
u := uintptr(p)
p = unsafe.Pointer(u + offset)

请注意,指针必须指向已分配的对象,因此它不能为nil.

// INVALID: conversion of nil pointer
u := unsafe.Pointer(nil)
p := unsafe.Pointer(uintptr(u) + offset)

(4)调用syscall.Syscall时将指针转换为uintptr.

软件包syscall中的Syscall函数将其uintptr参数直接传递给操作系统,然后,操作系统可以根据调用的详细信息将其中一些参数重新解释为指针. 也就是说,系统调用实现正在将某些参数从uintptr隐式转换回指针.

如果必须将指针参数转换为uintptr用作参数,则该转换必须出现在调用表达式本身中:

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

编译器通过安排引用的分配对象(如果有的话)被保留并且在调用完成之前不会移动,即使在类型调用中,它也处理在汇编中实现的函数的调用的参数列表中转换为uintptr的Pointer.似乎在调用过程中不再需要该对象.

为了使编译器能够识别这种模式,转换必须出现在参数列表中:

// INVALID: uintptr cannot be stored in variable
// before implicit conversion back to Pointer during system call.
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的Value方法返回uintptr而不是unsafe.Pointer类型,以防止调用者将结果更改为任意类型,而无需首先导入" unsafe". 但是,这意味着结果很脆弱,必须在调用后立即使用相同的表达式将其转换为Pointer:

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

与上述情况一样,在转换之前存储结果是无效的:

// INVALID: uintptr cannot be stored in variable
// before conversion back to Pointer.
u := reflect.ValueOf(new(int)).Pointer()
p := (*int)(unsafe.Pointer(u))

(6)将reflect.SliceHeader或reflect.StringHeader数据字段与指针进行转换.

与前面的情况一样,反射数据结构SliceHeader和StringHeader将字段Data声明为uintptr,以防止调用者将结果更改为任意类型,而无需首先导入" unsafe". 但是,这意味着SliceHeader和StringHeader仅在解释实际切片或字符串值的内容时才有效.

var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
hdr.Data = uintptr(unsafe.Pointer(p))              // case 6 (this case)
hdr.Len = n

在这种用法中,hdr.Data实际上是引用字符串标题中基础指针的替代方法,而不是uintptr变量本身.

通常,reflect.SliceHeader和reflect.StringHeader只能用作指向实际切片或字符串的* reflect.SliceHeader和* reflect.StringHeader,而不能用作纯结构. 程序不应声明或分配这些结构类型的变量.

// INVALID: a directly-declared header will not hold Data as a reference.
var hdr reflect.StringHeader
hdr.Data = uintptr(unsafe.Pointer(p))
hdr.Len = n
s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost
type Pointer *ArbitraryType

by  ICOPY.SITE