unsafe

安全指针

Go 语言的函数传参都是值传递

package main

import "fmt"

func double(x *int) {
	*x += *x
	x = nil//x是值拷贝,x和a都指向了一块内存地址,x=nil,并不影响a指向的内存地址中的数据
}

func main() {
	var a = 3
	double(&a)
	fmt.Println(a) // 6
	
	p := &a
	double(p)
	fmt.Println(a, p == nil) // 12 false
}

限制一:Go 的指针不能进行数学运算

a := 5
p := &a

p++  //会报编译错误:invalid operation
p = &a + 3

限制二:不同类型的指针不能相互转换

a := int(100)
var f *float64
f = &a //cannot use &a (type *int) as type *float64 in assignment

限制三:不同类型的指针不能使用 == 或 != 比较
只有在两个指针类型相同或者可以相互转换的情况下,才可以对两者进行比较。另外,指针可以通过 == 和 != 直接和 nil 作比较

限制四:不同类型的指针变量不能相互赋值

unsafe包

unsafe.Pointer非类型安全的指针,unsafe 包用于 Go 编译器,在编译阶段使用。
它可以绕过 Go 语言的类型系统,直接操作内存,使代码更高效。

unsafe.Pointer

通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(必须转换到某一类型的普通指针)。

uintpt

用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收

type uintptr uintpt

两者关系
unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。
只要将Pointer类型转换成uintptr类型,做完加减法后,转换成Pointer,通过*操作,取值,修改值,随意
unsafe.Pointer 可以让你的变量在不同的普通指针类型转来转去,也就是表示为任意可寻址的指针类型。而 uintptr 常用于与 unsafe.Pointer 打配合,用于做指针运算

unsafe原理

type ArbitraryType int  //任意的意思

type Pointer *ArbitraryType //类似于 C 语言里的 void*

unsafe 包还有其他三个函数

func Sizeof(x ArbitraryType) uintptr //返回类型 x 所占据的字节数,但不包含 x 所指向的内容的大小
func Offsetof(x ArbitraryType) uintptr //返回结构体成员在内存中的位置离结构体起始处的字节数,所传参数必须是结构体的成员
func Alignof(x ArbitraryType) uintptr //返回 m,m 是指当类型进行内存对齐时,它分配到的内存地址能整除 m

三个函数都是在编译期间执行,它们的结果可以直接赋给 const 型变量。另外,因为三个函数执行的结果和操作系统、编译器相关,所以是不可移植的

获取slice长度

调用 make 函数新建一个 slice,底层调用的是 makeslice 函数,返回的是 slice 结构体:

func makeslice(et *_type, len, cap int) slice
// runtime/slice.go
type slice struct {
    array unsafe.Pointer // 元素指针
    len   int // 长度 
    cap   int // 容量
}
func main() {
	sl := make([]int, 10, 20)
	l := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&sl)) + uintptr(8)))
	fmt.Println(l, len(sl))

	c := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&sl)) + uintptr(16)))
	fmt.Println(c, cap(sl))

获取map长度

调用make新建一个map,底层调用makemap 函数返回的是 hmap 的指针,注意是指针:

func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap

type hmap struct {
	count     int //元素个数,调用 len(map) 时,直接返回此值
	flags     uint8
	B         uint8
	noverflow uint16
	hash0     uint32

	buckets    unsafe.Pointer
	oldbuckets unsafe.Pointer
	nevacuate  uintptr

	extra *mapextra
}
func main() {
	mp := make(map[string]int, 5) //返回的是*hmap 指针
	mp["age"] = 18
	count := **(**int)(unsafe.Pointer(&mp))
}
posted @ 2021-10-03 09:47  wangzhilei  阅读(427)  评论(0编辑  收藏  举报