type ArbitraryType int type Pointer *ArbitraryType func Sizeof(x ArbitraryType) uintptr func Offsetof(x ArbitraryType) uintptr func Alignof(x ArbitraryType) uintptr
unsafe包只有两个类型,三个函数,可是功能很强大。html
unsafe 库让 golang 能够像C语言同样操做计算机内存,但这并非golang推荐使用的,能不用尽可能不用,就像它的名字所表达的同样,它绕过了golang的内存安全原则,是不安全的,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。golang
先简单介绍下Golang指针express
*类型:普通指针,用于传递对象地址,不能进行指针运算。安全
unsafe.Pointer:通用指针类型,用于转换不一样类型的指针,不能进行指针运算。函数
uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 没法持有对象。uintptr 类型的目标会被回收。ui
unsafe.Pointer 能够和 普通指针 进行相互转换。this
unsafe.Pointer 能够和 uintptr 进行相互转换。.net
也就是说 unsafe.Pointer 是桥梁,可让任意类型的指针实现相互转换,也能够将任意类型的指针转换为 uintptr 进行指针运算。指针
uintptr这个类型,在golang中,字节长度也是与int一致。一般Pointer不能参与运算,好比你要在某个指针地址上加上一个偏移量,Pointer是不能作这个运算的,那么谁能够呢?就是uintptr类型了,只要将Pointer类型转换成uintptr类型,作完加减法后,转换成Pointer,经过*操做,取值,修改值,随意。code
// ArbitraryType is here for the purposes of documentation only and is not actually // part of the unsafe package. It represents the type of an arbitrary Go expression. type ArbitraryType int type Pointer *ArbitraryType
ArbitraryType是int的一个别名,在Go中对ArbitraryType赋予特殊的意义。表明一个任意Go表达式类型。
Pointer 是int指针类型的一个别名,在Go中能够把Pointer类型,理解成任何指针的父类型。
下面为官方文档中对Pointer的使用场景介绍
Pointer represents a pointer to an arbitrary type. There are four special operationsavailable for type Pointer that are not available for other types: - 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 therefore allows a program to defeat the type system and read and write arbitrary memory. It should be used with extreme care.
golang的指针类型长度与int类型长度,在内存中占用的字节数是同样的.
ArbitraryType类型的变量也能够是指针。因此,千万不要死磕type后边的那个int
// 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. **func Sizeof(x ArbitraryType) uintptr** // Offsetof returns the offset within the struct of the field represented by x, // which must be of the form structValue.field. In other words, it returns the // number of bytes between the start of the struct and the start of the field. **func Offsetof(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(). **func Alignof(x ArbitraryType) uintptr**
经过分析发现,这三个函数的参数均是ArbitraryType类型,就是接受任何类型的变量。
unsafe.Sizeof函数返回的就是uintptr类型的值(表达式,即值的大小):
var p float64 = 99 fmt.Println(reflect.TypeOf(unsafe.Sizeof(p))) fmt.Println(unsafe.Sizeof(p))
results:
uintptr 8
unsafe.Sizeof接受任意类型的值(表达式),返回其占用的字节数,在上面的例子中float64的大小是8bytes。
若是传入一个指针类型的对象会返回多少呢?
type W struct { a byte b int32 c int64 } var w *W fmt.Println(unsafe.Sizeof(w)) //4 or 8
通常状况下,多是4或8,由于w是指针类型uintptr,而uintptr是平台相关的,在32位系统下大小是4bytes,在64位系统下是8bytes。
要获取值类型的大小,须要对指针变量进行取值:
fmt.Println(unsafe.Sizeof(*w)) //16
在上面的例子中,*w的大小为16,按照常理来讲,byte占用1字节,int32占用4字节,int64占用8字节,大小应该是13才对。这是由于发生了对齐,unsafe.Alignof能够计算对齐值:
unsafe.Alignof(w.a) // type byte unsafe.Alignof(w.b) // type int32 unsafe.Alignof(w.c) // type int64
分别是一、四、8,由于int32类型的对齐值是4bytes,必须是4的倍数,故byte类型要填充3个字节。而填充后,二者的大小和为8bytes,int64对齐值是8bytes,不须要填充,因此用unsafe.Sizeof获取到结构的大小为4+4+8=16。
反射包也有某些方法可用于计算对齐值:
unsafe.Alignof(w)等价于reflect.TypeOf(w).Align。
unsafe.Alignof(w.i)等价于reflect.Typeof(w.i).FieldAlign()。
若是咱们计算的是结构体的对齐值而不是某个字段或者基本类型,那么值会是多少呢?
type W struct { a byte b int32 c int64 } var w *W var w2 W fmt.Println(unsafe.Alignof(w)) fmt.Println(unsafe.Alignof(w2)) fmt.Println(reflect.TypeOf(w).Elem().Align())
results:
8 8 8
64位机器下,指针对象的对齐值是8,由于指针类型是uintptr。而结构体的值类型倒是8bytes的对齐值,这是由于会先进行字段的对齐,字段最大的对齐值是8bytes,所以结构体值类型的对齐值也是8。
更改结构,验证一下:
type W struct { a byte b int32 c int32 } var w W fmt.Println(unsafe.Alignof(w)) //4
type T struct { t1 byte t2 int32 t3 int64 t4 string t5 bool } fmt.Println("----------unsafe.Pointer---------") t := &T{1, 2, 3, "this is a example", true} ptr := unsafe.Pointer(t) t1 := (*byte)(ptr) fmt.Println(*t1) t2 := (*int32)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t2))) *t2 = 99 fmt.Println(t) t3 := (*int64)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t3))) fmt.Println(*t3) *t3 = 123 fmt.Println(t)
results:
----------unsafe.Pointer--------- 1 &{1 99 3 this is a example true} 3 &{1 99 123 this is a example true}
借助于 unsafe.Pointer,咱们实现了像 C 语言中的指针偏移操做。能够看出,这种不安全的操做使得咱们能够在任何地方直接访问结构体中未公开的成员,只要能获得这个结构体变量的地址。