英文:
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)
}
运行时发生了运行时恐慌(panic):
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0xa pc=0x43dd4d]
为什么会出现这个错误呢?
英文:
The code is:
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)
}
When running it, a run-time panic occurs:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0xa pc=0x43dd4d]
Why?
答案1
得分: 5
因为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 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
是int
值100
的4个字节)。
如果使用切片描述符的地址,您将看到恐慌,因为描述符包含诸如第一个元素的地址、元素计数和容量之类的内容。因此,通过使用描述符的地址,您将修改这些内容,并且当尝试再次将其作为切片打印时,您写入的数据很可能是无效指针。这由您写入值10
(十六进制为0xa
)以及错误消息包含的addr=0xa
来确认。
另一个选项是使用一个“真正的”数组而不是切片,因为数组不是描述符:
buf := [50]byte{}
t := (*Point)(unsafe.Pointer(&buf))
通过这个更改,输出将是相同的。
英文:
Because buf
is a slice (not an array), and slices are just descriptors.
You most likely wanted to write to the memory space of the allocated bytes, so instead of using the address of the slice descriptor, use the address of the allocated bytes, which you can get by taking the address of the first element (a slice describes a contiguous section of an underlying array):
t := (*Point)(unsafe.Pointer(&buf[0]))
Output (try it on the 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]
As you can see, data you set appears in the 2nd output (10 0 0 0
are the 4 bytes of the int
value 10
, 100 0 0 0
are the 4 bytes of the int
value 100
).
You see panic if you use the address of the slice descriptor because the descriptor contains things like the address of the first element, the element count and capacity. So by using the address of the descriptor you will modify these and when trying to print it again as a slice, the data you wrote to it will most likely be in invalid pointer. This is confirmed by you writing the value 10
which is 0xa
in hex and the error message contains: addr=0xa
Another option would be to use a "real" array instead of a slice because arrays are not descriptors:
buf := [50]byte{}
t := (*Point)(unsafe.Pointer(&buf))
And with this change the output will be the same.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论