在 Go 中使用 unsafe.Pointer 时出现恐慌
Panic using unsafe.Pointer in Go
密码是:
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)
}
当 运行ning 时,会发生 运行 次恐慌:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0xa pc=0x43dd4d]
为什么?
因为buf
是切片(不是数组),切片只是描述符。
您很可能想写入已分配字节的内存 space,因此不要使用切片描述符的地址,而是使用已分配字节的地址,您可以通过获取第一个元素的地址(切片描述基础数组的 连续 部分):
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
值的4个字节10
,100 0 0 0
是4个字节int
值的字节 100
).
如果您使用切片描述符的地址,您会看到恐慌,因为描述符包含诸如第一个元素的地址、元素计数和容量之类的内容。因此,通过使用描述符的地址,您将修改这些内容,并且当您尝试将其作为切片再次打印时,您写入的数据很可能位于无效指针中。这是通过您写入值 10
确认的,该值是 0xa
的十六进制并且错误消息包含:addr=0xa
另一种选择是使用 "real" 数组而不是切片,因为数组不是描述符:
buf := [50]byte{}
t := (*Point)(unsafe.Pointer(&buf))
经过此更改,输出将相同。
密码是:
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)
}
当 运行ning 时,会发生 运行 次恐慌:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0xa pc=0x43dd4d]
为什么?
因为buf
是切片(不是数组),切片只是描述符。
您很可能想写入已分配字节的内存 space,因此不要使用切片描述符的地址,而是使用已分配字节的地址,您可以通过获取第一个元素的地址(切片描述基础数组的 连续 部分):
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
值的4个字节10
,100 0 0 0
是4个字节int
值的字节 100
).
如果您使用切片描述符的地址,您会看到恐慌,因为描述符包含诸如第一个元素的地址、元素计数和容量之类的内容。因此,通过使用描述符的地址,您将修改这些内容,并且当您尝试将其作为切片再次打印时,您写入的数据很可能位于无效指针中。这是通过您写入值 10
确认的,该值是 0xa
的十六进制并且错误消息包含:addr=0xa
另一种选择是使用 "real" 数组而不是切片,因为数组不是描述符:
buf := [50]byte{}
t := (*Point)(unsafe.Pointer(&buf))
经过此更改,输出将相同。