unsafe 库使用小结

unsafe 库让 golang 可以像 C 语言一样操作计算机内存,但这并不是 golang 推荐使用的,能不用尽量不用,就像它的名字所表达的一样,它绕过了golang的内存安全原则,是不安全的,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。

unsafe 包的内容不多,下面就它提供的函数进行说明

unsafe.Alignof

获取变量的对齐值,除 int、uintptr 这些依赖CPU位数的类型,基本类型的对齐值都是固定的。 结构体的对齐值取他的成员对齐值的最大值。可以通过下面示例打印出相应的对齐值(实验机器是64位机器)。

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)))
类型对齐值
byte1
bool1
int81
uint81
int324
int648
uint324
uint648
uintptr8
float328
float648
complex648
complex1288
chan8
slice8
map8
structit 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, 2, 3, "", 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, 2, 3, "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 语言中的指针偏移操作。可以看出,这种不安全的操作使得我们可以在任何地方直接访问结构体中未公开的成员,只要能得到这个结构体变量的地址。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值