unsafe包是不安全的,可以绕过go内存安全机制,直接对内存进行读写。
go 语言是强类型的,所以一般情况不允许不同类型指针进行转换
func main() {
i:= 10
ip:=&i
var fp *float64 = (*float64)(ip)
fmt.Println(fp)
}
// cannot convert ip (type *int) to type *float64
如果需要转换的时候,需要用的unsafe.Pointer
unsafe.Pointer 是一种特殊意义的指针,可以表示任意类型的地址
我们使用unsafe.Pointer,就可以将int指针改为float64的指针并进行运算,下面是3倍乘法运算。
func main() {
i:= 10
ip:=&i
var fp *float64 = (*float64)(unsafe.Pointer(ip))
*fp = *fp * 3
fmt.Println(i) // 30
}
// 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 可以表示任何类型, 而 unsafe.Pointer 又是 *ArbitraryType,也就是说 unsafe.Pointer 是任何类型的指针,也就是一个通用型的指针。
unsafe.Pointer可以表示指针了,但是 unsafe.Pointer 不能进行运算,比如不支持 +(加号)运算符操作,
这个时候就需要用到 uintptr
在 builtin/builtin.go 包中,是一种类型
// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr
通过uintptr,可以对指针偏移进行计算, 这样就可以像c语言一样访问内存,对内存进行读写,真正操作内存。
func main() {
type person struct {
name string
age int
}
p := new(person)
pName := (*string)(unsafe.Pointer(p)) // 偏移量为0
*pName = "test"
pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(p))+unsafe.Offsetof(p.age))) // 指针偏移
*pAge = 20
fmt.Println(p) //&{test 20}
}
对一个结构体进行内存操作赋值。
age 字段不是 person 的第一个字段,要修改它必须要进行指针偏移运算。所以需要先把指针变量 p 通过 unsafe.Pointer 转换为 uintptr, 这样才能进行地址运算.这个偏移量可以通过函数 unsafe.Offsetof 计算出来,该函数返回的是一个 uintptr 类型的偏移量, 有了这个偏移量就可以通过 + 号运算符获得正确的 age 字段的内存地址了,也就是通过 unsafe.Pointer 转换后的 *int 类型的指针变量 pAge。
注意:
如果要进行指针运算,要先通过 unsafe.Pointer 转换为 uintptr 类型的指针。 指针运算完毕后,还要通过 unsafe.Pointer 转换为真实的指针类型(比如示例中的 *int 类型), 这样可以对这块内存进行赋值或取值操作。
指针运算的核心在于它操作的是一个个内存地址,通过内存地址的增减,就可以指向一块块不同的内存并对其进行操作,而且不必知道这块内存被起了什么名字(变量名)
Go 语言中存在三种类型的指针: 常用的 *T、unsafe.Pointer 及 uintptr。
Sizeof 函数可以返回一个类型所占用的内存大小,这个大小只与类型有关, 和类型对应的变量存储的内容大小无关,比如 bool 型占用一个字节、int8 也占用一个字节。
fmt.Println(unsafe.Sizeof(true))
fmt.Println(unsafe.Sizeof(int8(0)))
fmt.Println(unsafe.Sizeof(int16(10)))
fmt.Println(unsafe.Sizeof(int32(10000000)))
fmt.Println(unsafe.Sizeof(int64(10000000000000)))
fmt.Println(unsafe.Sizeof(int(10000000000000000)))
对于和平台有关的 int 类型,要看平台是 32 位还是 64 位,会取最大的