一、golang指针及其限制
1.与 C语言 相比,在Go语言中没有最复杂的指针运算部分,只留下了获取指针(&运算符)和获取对象(*运算符)的运算。而且Go语言中没有->操作符来调用指针所属的成员,而与一般对象一样,都是使用.来调用。
2.Go语言中一个指针默认值为 nil
3.golang指针限制
1)Go的指针不能进行数学运算
2)不同类型的指针不能相互转换
3)不同类型的指针不能使用 == 或 != 比较
4)不同类型的指针变量不能相互赋值
二、unsafe包
golang指针是类型安全的,但存在很多限制。因此Go还提供了一种非类型安全的指针,即unsafe.Pointer。
unsafe.Pointer可以指向任意类型,类似于 C 语言里的 void*。在unsafe包中除了Pointer外还有其他三个函数:
func Sizeof(x ArbitraryType) uintptr:返回类型 x 所占据的字节数,但不包含 x 所指向的内容的大小。例如,对于一个指针,函数返回的大小为 8 字节(64位机上),一个 slice 的大小则为 slice header 的大小。
func Offsetof(x ArbitraryType) uintptr:返回 结构体 成员在内存中的位置离结构体起始处的字节数,所传参数必须是结构体的成员。
func Alignof(x ArbitraryType) uintptr:返回 m,m 是指当类型进行内存对齐时,它分配到的内存地址能整除 m。
以上三个函数返回的结果都是 uintptr 类型,这和 unsafe.Pointer 可以相互转换。

pointer 不能直接进行数学运算,但可以把它转换成 uintptr,对 uintptr 类型进行数学运算,再转换成 pointer 类型。
注意:uintptr 并没有指针的语义,意思就是 uintptr 所指向的对象会被 gc 无情地回收。而 unsafe.Pointer 有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收。
三、unsafe的应用示例
1.使用unsafe获取slice长度
type slice struct {
array unsafe.Pointer // 元素指针
len int // 长度
cap int // 容量
}
package main
import “fmt”
func main() {
s := make([]int, 10)
var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8))) //先转为uintptr,然后进行指针运算,移动slice的第二个长度的成员变量上,获取其指针指向的值
fmt.Println(Len, len(s)) // 10 10
}
2.通过unsafe.Offsetof方法获取成员 偏移量
对于一个结构体,通过 offset 函数可以获取结构体成员的偏移量,进而获取成员的地址,读写该地址的内存,就可以达到改变成员值的目的。结构体在分配内存时会得到一块连续的内存空间,结构体的地址也代表了第一个成员的地址。
package main
import (
“fmt”
“unsafe”
)
type Student struct {
name string
sex string
}
func main() {
p := Student{“zhangsan”, “male”}
fmt.Println(p) //{stefno male}
name := (*string)(unsafe.Pointer(&p))
*name = “lisi”
sex := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.sex)))
*sex = “female”
fmt.Println(p) //{lisi female}
}