代码是:
package main
import (
"fmt"
"unsafe"
)
type Point struct {
x int
y int
}
func main() {
buf := make([]byte, 50)
fmt.Println(buf)
t := (*Point)(unsafe.Pointer(&buf))
t.x = 10
t.y = 100
fmt.Println(buf)
}
运行时,会发生运行时恐慌:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0xa pc=0x43dd4d]
为什么?
答案 0 :(得分:4)
因为buf
是切片(不是数组),切片只是描述符。
您很可能想要写入已分配字节的内存空间,因此不使用切片描述符的地址,而是使用已分配字节的地址,您可以通过获取第一个元素的地址来获取该地址(一个切片描述了底层数组的连续部分:
t := (*Point)(unsafe.Pointer(&buf[0]))
输出(在Go Playground上尝试):
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[10 0 0 0 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
如您所见,您设置的数据显示在第二个输出中(10 0 0 0
是int
值10
的4个字节,100 0 0 0
是4个字节的int
值100
)。
如果使用切片描述符的地址,则会看到混乱,因为描述符包含第一个元素的地址,元素数和容量等内容。因此,通过使用描述符的地址,您将修改它们,并且当尝试再次将其作为切片打印时,您写入它的数据很可能是无效指针。您通过以十六进制编写10
值0xa
来确认,错误消息包含:addr=0xa
另一个选择是使用"真正的"数组而不是切片,因为数组不是描述符:
buf := [50]byte{}
t := (*Point)(unsafe.Pointer(&buf))
通过此更改,输出将是相同的。