unsafe 库让 golang 可以像 C 语言一样操作计算机内存,但这并不是 golang 推荐使用的,能不用尽量不用,就像它的名字所表达的一样,它绕过了golang的内存安全原则,是不安全的,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。
unsafe.Alignof
获取变量的对齐值,除 int、uintptr 这些依赖cpu位数的类型,基本类型的对齐值都是固定的。 结构体的对齐值取他的成员对齐值的最大值。可以通过下面示例打印出相应的对齐值(实验机器是64位机器)。
e.g.
fmt.Println(unsafe.Alignof(byte(0)))
fmt.Println(unsafe.Alignof(int8(0)))
fmt.Println(unsafe.Alignof(uint8(0)))
fmt.Println(unsafe.Alignof(int16(0)))
fmt.Println(unsafe.Alignof(uint16(0)))
fmt.Println(unsafe.Alignof(int32(0)))
fmt.Println(unsafe.Alignof(uint32(0)))
fmt.Println(unsafe.Alignof(int64(0)))
fmt.Println(unsafe.Alignof(uint64(0)))
fmt.Println(unsafe.Alignof(uintptr(0)))
fmt.Println(unsafe.Alignof(float32(0)))
fmt.Println(unsafe.Alignof(float64(0)))
//fmt.Println(unsafe.Alignof(complex(0,0)))
fmt.Println(unsafe.Alignof(complex64(0)))
fmt.Println(unsafe.Alignof(complex128(0)))
fmt.Println(unsafe.Alignof(""))
fmt.Println(unsafe.Alignof(new(int)))
fmt.Println(unsafe.Alignof(struct {
f float32
ff float64
}{}))
fmt.Println(unsafe.Alignof(make(chan bool,10)))
fmt.Println(unsafe.Alignof(make([]int,10)))
fmt.Println(unsafe.Alignof(make(map[string]string,10)))
类型 | 对齐值 |
---|---|
byte | 1 |
bool | 1 |
int8 | 1 |
uint8 | 1 |
int32 | 4 |
int64 | 8 |
uint32 | 4 |
uint64 | 8 |
uintptr | 8 |
float32 | 8 |
float64 | 8 |
complex64 | 8 |
complex128 | 8 |
chan | 8 |
slice | 8 |
map | 8 |
struct | it depends on the max align among its members |
unsafe.Sizeof
查看变量所占字节数。以下面例子简要说明。
type T struct {
t1 byte
t2 int32
t3 int64
t4 string
t5 bool
}
func main() {
t := &T{1,2,3,"",true}
fmt.Println(unsafe.Sizeof(*t))
fmt.Println(unsafe.Sizeof(t.t1))
fmt.Println(unsafe.Sizeof(t.t2))
fmt.Println(unsafe.Sizeof(t.t3))
fmt.Println(unsafe.Sizeof(t.t4))
fmt.Println(unsafe.Sizeof(t.t5))
}
结果:
40 1 4 8 16 1
这里以0x0作为基准内存地址。打印出来总共占用40个字节。t.t1 为 char,对齐值为 1,0x0 % 1 == 0,从0x0开始,占用一个字节;t.t2 为 int32,对齐值为 4,0x4 % 4 == 0,从 0x4 开始,占用 4 个字节;t.t3 为 int64,对齐值为 8,0x8 % 8 == 0,从 0x8 开始,占用 8 个字节;t.t4 为 string,对齐值为 8,0x16 % 8 == 0,从 0x16 开始, 占用 16 个字节(string 内部实现是一个结构体,包含一个字节类型指针和一个整型的长度值);t.t5 为 bool,对齐值为 1,0x32 % 8 == 0,从 0x32 开始,占用 1 个字节。从上面分析,可以知道 t 的对齐值为 8,最后 bool 之后会补齐到 8 的倍数,故总共是 40 个字节。
unsafe.Offsetof
查看结构体成员的偏移字节数。以上面这个例子中结构体 T 为例。
func main() {
t := &T{1,true}
fmt.Println(unsafe.Offsetof(t.t1))
fmt.Println(unsafe.Offsetof(t.t2))
fmt.Println(unsafe.Offsetof(t.t3))
fmt.Println(unsafe.Offsetof(t.t4))
fmt.Println(unsafe.Offsetof(t.t5))
}
结果
0 4 8 16 32
分析如上 unsafe.Sizeof 中说明。
unsafe.Pointer
这个主要用于不同指针类型之间进行强制类型转换。它是一个中介者,不同指针类型不能直接进行转换,只能通过它中转一下。但是它还无法直接进行指针运算,必须将其转化成 uintptr 类型才能进行指针的运算,uintptr 与 unsafe.Pointer 之间可以相互转换。
这里仍然取上面定义的结构体 T 进行说明。
t := &T{1,"this is a example",true}
ptr := unsafe.Pointer(t)
t1 := (*byte)(ptr)
*t1 = 4
t2 := (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) + unsafe.Offsetof(t.t2)))
*t2 = 99
fmt.Println(t)
t3 := (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) + unsafe.Offsetof(t.t3)))
*t3 = 333
fmt.Println(t)
借助于 unsafe.Pointer,我们实现了像 C 语言中的指针偏移操作。可以看出,这种不安全的操作使得我们可以在任何地方直接访问结构体中未公开的成员,只要能得到这个结构体变量的地址。
原文链接:https://www.f2er.com/go/190089.html