Go语言中的unsafe包非常简单,但是威力强大,用不少就出问题。整个包就这些代码:

1
2
3
4
5
6
7
package unsafe

type ArbitraryType int
type Pointer *ArbitraryType
func Sizeof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Alignof(x ArbitraryType) uintptr

一个指针就能让你在内存的海洋随意穿梭,没有限制,你走到哪里看到哪里,想从什么角度看就给你呈现什么角度的值。这让我想起了《黑客帝国》中的巡游机器人…

示例

下面这个例子,加深对数据类型,和unsafe.Pointer的认识。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type (
	dataType       struct{}
	emptyInterface struct {
		typ *dataType
		ptr unsafe.Pointer
	}
)

func UnsafeAddr() {
	var num = int32(10)
	pNum := &num
	ptr := unsafe.Pointer(pNum)

	println(num)
	println(ptr)
	println(uintptr(ptr))

	*(*int32)(ptr) = 11
	println(num)

	println("++++++++++++++++++")
	var inter interface{}
	inter = pNum
	println(inter)

	println("++++++++++++++++++")
	pp := (*emptyInterface)(unsafe.Pointer(&inter))
	println(pp.typ)
	println(pp.ptr)
}

结果如下:

10
0xc00009bf0c      
824634359564      
11
++++++++++++++++++
(0x76cc60,0xc00009bf0c)
++++++++++++++++++
0x76cc60
0xc00009bf0c

下面是调试时,变量值情况:

image-20230523113110934

string不可改写

看下面的实验,我们试图修改字符串中某个字符,不管是正常访问,还是通过黑魔法将字符串转换成byte切片再访问,结果都一样,抛异常,不让修改。但底层数据存储是byte切片的时候,都是能改的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func BTS(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func STB(s string) (b []byte) {
	sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
	return b
}

var bytSrc = []byte("bbbbbb")
var strSrc = "aaaaaa"

func StringBytesTrans() {
	// 底层是字符串,可读取,修改抛异常
	//var list = strSrc
	var list = STB(strSrc)

	// 底层是byte切片,读取或修改,都可以
	//var list = bytSrc
	//var list = BTS(bytSrc)

	usp := unsafe.Pointer(&list)
	fmt.Println(*(*int)(unsafe.Pointer(uintptr(usp) + 8)))

	add := (*byte)(unsafe.Pointer(*(*uintptr)(usp) + 1))
	fmt.Println(*add)

	*add = 109
	fmt.Printf("%s", list)
}

结果如下:

6
98    
bmbbbb # 此处的打印,如果底层是字符串存储的,都会抛异常

(完)