在Go中使用unsafe.Pointer的恐慌

时间:2015-08-11 07:40:24

标签: pointers go slice unsafe

代码是:

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]

为什么?

1 个答案:

答案 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 0int10的4个字节,100 0 0 0是4个字节的int100)。

如果使用切片描述符的地址,则会看到混乱,因为描述符包含第一个元素的地址,元素数和容量等内容。因此,通过使用描述符的地址,您将修改它们,并且当尝试再次将其作为切片打印时,您写入它的数据很可能是无效指针。您通过以十六进制编写100xa来确认,错误消息包含:addr=0xa

另一个选择是使用"真正的"数组而不是切片,因为数组不是描述符:

buf := [50]byte{}
t := (*Point)(unsafe.Pointer(&buf))

通过此更改,输出将是相同的。