在Go语言中使用unsafe.Pointer引发恐慌

huangapple go评论84阅读模式
英文:

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 0int10的4个字节,100 0 0 0int100的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.

huangapple
  • 本文由 发表于 2015年8月11日 15:40:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/31935915.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定