[golang] 使用 unsafe 转换 int 为 byte 切片,在通过打印读 byte 切片时数据发生变化

2022-07-19 09:38:23 +08:00
 RayR
func main() {
	n := 1

	nbytes := i2bin(n)

	for i := 0; i < len(nbytes); i++ {
		fmt.Printf("value: %v,addr: %p", nbytes[i], &nbytes[i])
	}
}

func i2bin(i int) []byte {
	sh := &reflect.SliceHeader{Len: 8, Cap: 8, Data: uintptr(unsafe.Pointer(&i))}
	return *(*[]byte)(unsafe.Pointer(sh))
}

如题,转换完成后数据是正常的为: [0]:0x1 [1]:0x0 [2]:0x0 [3]:0x0 [4]:0x0 [5]:0x0 [6]:0x0 [7]:0x0

进入循环,第一次打印数组结束数据就变成了: [0]:0x9 [1]:0x10 [2]:0xa3 [3]:0x0 [4]:0x0 [5]:0x0 [6]:0x0 [7]:0x0

为什么会发生变化呢?

golang: v1.18.3 windows

1038 次点击
所在节点    Go 编程语言
3 条回复
joesonw
2022-07-19 09:58:40 +08:00
按值传进去的,在栈上。
RayR
2022-07-19 10:21:48 +08:00
@joesonw

把`i2bin`写在 main 函数中确实访问后没有变化。通过指针传入 int 也可以达到这个效果,意味着这片内存会逃逸到堆上。

所以不论是在`i2bin`中创建完的 SliceHeader 还是`main`中响应过程中复制的切片,`uintptr`指向的是同一片内存空间。在函数执行完毕后`main`函数的切片依旧指向的是栈上的空间,但是这片空间已经被 runtime 标记为可回收的空间并且确实被回收了。
lysS
2022-07-27 10:05:43 +08:00
var sb = make([]byte, 8)
*(*int)(unsafe.Pointer(&sb[0])) = i

而且可以不取 0 ,这样可以避免一次中间内存的分配

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/867173

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX