NOTE
1.8 unsafe
1. 指针类型 1.1. 是什么 变量的地址 1.2. 为什么需要指针类型 使用指针类型可以让callee修改caller传递的值 1.3. Golang的指针类型 Golang的指针类型是安全的 - 不能进行数学运算 - 不同类型的指针不能相互转换 - 不同类型的指针不能使用 == 或 != 比较
这是历史学习笔记,可能存在过时或不完整的理解。
1. 指针类型
1.1. 是什么
变量的地址
1.2. 为什么需要指针类型
使用指针类型可以让callee修改caller传递的值
func double(x int) {
x += x
}
func main() {
var a = 3
double(a)
fmt.Println(a) // 3
}
//修改
func double(x *int) {
*x += *x
x = nil
}
func main() {
var a = 3
double(&a)
fmt.Println(a) // 6
p := &a
double(p)
fmt.Println(a, p == nil) // 12 false
}
1.3. Golang的指针类型
Golang的指针类型是安全的
- 不能进行数学运算
- 不同类型的指针不能相互转换
- 不同类型的指针不能使用 == 或 != 比较
- 不同类型的指针变量不能相互赋值
2. unsafe
2.1. 是什么
不安全的指针类型
2.2. 使用
安全的指针类型无法进行计算,可以先转换成unsafe.Pointer,再转换成uintptr进行计算
2026 注:下面直接读取 slice / map 内部布局的例子依赖具体 Go 版本和架构,只适合作为当时的实现实验,不应当作稳定接口使用。
2.2.1. 获取slice的长度
func TestUnsafe1(t *testing.T) {
s := make([]int, 9, 20)
//Len: &s => pointer => uintptr => pointer => *int => int
var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
fmt.Println(Len, len(s))
//Cap: &s => pointer => uintptr => pointer => *int => int
var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))
fmt.Println(Cap, cap(s))
}
//输出
9 9
20 20
2.2.2. 获取map的长度
func TestUnsafe2(t *testing.T) {
mp := make(map[string]int)
mp["qcrao"] = 100
mp["stefno"] = 18
//&mp => pointer => **int => int
count := **(**int)(unsafe.Pointer(&mp))
fmt.Println(count, len(mp))
}
// 输出
2 2
2.2.3. string和[]byte转换
func TestUnsafe3(t *testing.T) {
bytes := []byte{104, 101, 108, 108, 111}
p := unsafe.Pointer(&bytes) //强制转换成unsafe.Pointer,编译器不会报错
str := *(*string)(p) //然后强制转换成string类型的指针,再将这个指针的值当做string类型取出来
fmt.Println(str) //输出 "hello
}
2.3. 原理
- 数据结构
type ArbitraryType int
type Pointer *ArbitraryType//类似于C语言的void*
- 方法
//返回类型 x 所占据的字节数,但不包含 x 所指向的内容的大小
func Sizeof(x ArbitraryType) uintptr
//返回结构体成员在内存中的位置离结构体起始处的字节数,所传参数必须是结构体的成员
func Offsetof(x ArbitraryType) uintptr
//返回 m,m 是指当类型进行内存对齐时,它分配到的内存地址能整除 m
func Alignof(x ArbitraryType) uintptr