Table of Contents generated with DocToc
- *类型:普通指针类型,用于传递对象地址,不能进行指针运算。
- unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(必须转换到某一类型的普通指针)。
- uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。
Go 语言类型系统是为了安全和效率设计的,有时,安全会导致效率低下。有了 unsafe 包,高阶的程序员就可以利用它绕过类型系统的低效。 因此,它就有了存在的意义,阅读 Go 源码,会发现有大量使用 unsafe 包的例子。 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。 unsafe.Pointer 可以让你的变量在不同的普通指针类型转来转去,也就是表示为任意可寻址的指针类型。 而 uintptr 常用于与 unsafe.Pointer 打配合,用于做指针运算
- unsafe.Pointer 通用指针
- (1)任何类型的指针都可以被转化为Pointer
- (2)Pointer可以被转化为任何类型的指针
- (3)uintptr可以被转化为Pointer
- (4)Pointer可以被转化为uintptr
Note : 我们不可以直接通过*p来获取unsafe.Pointer指针指向的真实变量的值,因为我们并不知道变量的具体类型。 和普通指针一样,unsafe.Pointer指针也是可以比较的,并且支持和nil常量比较判断是否为空指针
- uintptr 整数类型
定义: uintptr is an integer type that is large enough to hold the bit pattern of any 03PointerSetPrivateValue
源码:
type uintptr uintptr
Note:uintptr 并没有指针的语义,意思就是 uintptr 所指向的对象会被 gc 无情地回收。 而 unsafe.Pointer 有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收
unsafe包 两个类型,三个函数
type ArbitraryType int
type Pointer *ArbitraryType
ArbitraryType是int的一个别名,在Go中对ArbitraryType赋予特殊的意义。代表一个任意Go表达式类型。实际上它类似于 C 语言里的 void*。 Pointer是int指针类型的一个别名,在Go中可以把Pointer类型,理解成任何指针的父类型。
// Sizeof takes an expression x of any type and returns the size in bytes
// of a hypothetical variable v as if v was declared via var v = x.
// The size does not include any memory possibly referenced by x.
// For instance, if x is a slice, Sizeof returns the size of the slice
// descriptor, not the size of the memory referenced by the slice.
// The return value of Sizeof is a Go constant.
func Sizeof(x ArbitraryType) uintptr
unsafe.Sizeof接受任意类型的值(表达式),返回其占用的字节数,这和c语言里面不同,
如果是slice,则不会返回这个slice在内存中的实际占用长度,一个 slice 的大小则为 slice header 的大小. c语言里面sizeof函数的参数是类型,而这里是一个表达式,比如一个变量。
int a=10;
int arr=[1,2,3];
char str[]="hello";
int len_a = sizeof(a);
int len_arr = sizeof(arr);
int len_str = sizeof(str)
printf("len_a=%d,len_arr=%d,len_str=%d\n",len_a,len_arr,len_str)
返回类型 x 所占据的字节数,但不包含 x 所指向的内容的大小。例如,对于一个指针,函数返回的大小为 8 字节(64位机上)
func Offsetof(x ArbitraryType) uintptr
//unsafe.Offsetof: 返回结构体成员在内存中的位置离结构体起始处的字节数,所传参数必须是结构体的成员
func Alignof(x ArbitraryType) uintptr
//Alignof返回变量对齐字节数量m,Offsetof返回变量指定属性的偏移量,它分配到的内存地址能整除 m.
//这个函数虽然接收的是任何类型的变量,但是有一个前提,就是变量要是一个struct类型,且还不能直接将这个struct类型的变量当作参数,
//只能将这个struct类型变量的属性当作参数
规则:
- 对于任意类型的变量 x ,unsafe.Alignof(x) 至少为 1。
- 对于 struct 类型的变量 x,计算 x 每一个字段 f 的 unsafe.Alignof(x.f),unsafe.Alignof(x) 等于其中的最大值。
type Bar struct {
x int32 // 4
y *Foo // 8
z bool // 1
}
// 结构体变量b1的对齐系数
fmt.Println(unsafe.Alignof(b1)) // 8
// b1每一个字段的对齐系数
fmt.Println(unsafe.Alignof(b1.x)) // 4:表示此字段须按4的倍数对齐
fmt.Println(unsafe.Alignof(b1.y)) // 8:表示此字段须按8的倍数对齐
fmt.Println(unsafe.Alignof(b1.z)) // 1:表示此字段须按1的倍数对
- 对于 array 类型的变量 x,unsafe.Alignof(x) 等于构成数组的元素类型的对齐倍数。
总结: 三个函数的参数均是ArbitraryType类型,就是接受任何类型的变量,返回的结果都是 uintptr 类型,这和 unsafe.Pointer 可以相互转换。
三个函数都是在编译期间执行,它们的结果可以直接赋给 const型变量。另外,因为三个函数执行的结果和操作系统、编译器相关,所以是不可移植的
例如,一般我们不能操作一个结构体的未导出成员,但是通过 unsafe 包就能做到。 unsafe 包让我可以直接读写内存,还管你什么导出还是未导出
//mapaccess1、mapassign、mapdelete 函数中,需要定位 key 的位置,会先对 key 做哈希运算。
b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + (hash&m)*uintptr(t.bucketsize)))
h.buckets 是一个 unsafe.Pointer,将它转换成 uintptr,然后加上 (hash&m)*uintptr(t.bucketsize), 二者相加的结果再次转换成 unsafe.Pointer,最后,转换成 bmap指针,得到 key 所落入的 bucket 位置
// store new key/value at insert position
if t.indirectkey {
kmem := newobject(t.key)
*(*unsafe.Pointer)(insertk) = kmem
insertk = kmem
}
if t.indirectvalue {
vmem := newobject(t.elem)
*(*unsafe.Pointer)(val) = vmem
}
typedmemmove(t.key, insertk, key)
这段代码是在找到了 key 要插入的位置后,进行“赋值”操作。insertk 和 val 分别表示 key 和 value 所要“放置”的地址。
如果 t.indirectkey 为真,说明 bucket 中存储的是 key 的指针,因此需要将 insertk 看成 指针的指针,
这样才能将 bucket 中的相应位置的值设置成指向真实 key 的地址值,也就是说 key 存放的是指针
注意 uintptr 并没有指针的语义,意思就是 uintptr 所指向的对象会被 gc 无情地回收。而 unsafe.Pointer 有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收
atomic/value.go中定义了一个ifaceWords结构,其中typ和data字段类型就是unsafe.Poniter, 这里使用unsafe.Poniter类型的原因是传入的值就是interface{}类型,使用unsafe.Pointer强转成ifaceWords类型,这样可以把类型和值都保存了下来
// ifaceWords is interface{} internal representation.
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
// Load returns the value set by the most recent Store.
// It returns nil if there has been no call to Store for this Value.
func (v *Value) Load() (x interface{}) {
vp := (*ifaceWords)(unsafe.Pointer(v))
for {
typ := LoadPointer(&vp.typ) // 读取已经存在值的类型
/**
..... 中间省略
**/
// First store completed. Check type and overwrite data.
if typ != xp.typ { //当前类型与要存入的类型做对比
panic("sync/atomic: store of inconsistently typed value into Value")
}
}