go unsafe 包

go unsafe 包

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 是一种特殊意义的指针,可以表示任意类型的地址

我们使用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

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。

  1. 任何类型的 *T 都可以转换为 unsafe.Pointer;
  2. unsafe.Pointer 也可以转换为任何类型的 *T;
  3. unsafe.Pointer 可以转换为 uintptr;
  4. uintptr 也可以转换为 unsafe.Pointer。

unsafe.Sizeof

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 位,会取最大的

参考

22 讲通关 Go 语言

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章