在写入但不从golang中的存储位置读取时读取SIGSEGV

时间:2019-03-01 22:43:48

标签: go

给我的印象是,使用unsafe包可以读取/写入任意数据。我正在尝试更改interface{}所指向的值,而不更改指针本身。

假设interface{}的实现方式为

type _interface struct {
    type_info *typ
    value unsafe.Pointer
}

尽管读取成功,但设置SIGSEGV失败。

func data(i interface{}) unsafe.Pointer {
    return unsafe.Pointer((*((*[2]uintptr)(unsafe.Pointer(&i))))[1])
}

func main() {
    var i interface{}
    i = 2
    fmt.Printf("%v, %v\n", (*int)(data(i)), *(*int)(data(i)))
    *((*int)(data(i))) = 3
}

我做错什么了吗,或者在golang中这不可能吗?

1 个答案:

答案 0 :(得分:1)

嗯...这是目前我如何理解您的第二个代码示例,以防万一我出错了(如果您发现我所描述的内容有误,我的答案可能是无法挽回的错误,您应该忽略其余部分我必须说的话。

  1. i中为接口main分配内存。
  2. i的值设置为值为2的整数类型。
  3. i中为接口data分配内存。
  4. 将main的i的值复制到data的i;也就是说,将新接口的值设置为值为2的整数类型。
  5. 将新变量的地址放到指向uintptr的length-2数组的指针中(以unsafe.Pointer作为中介,它迫使编译器接受此强制转换)。
  6. 将数组的第二个元素(其值是{{1}中i的值部分的地址)放回到data中并返回。

我已经在更多步骤中使an attempt做相同的事情,但是不幸的是,我遇到了所有相同的问题:程序认识到我有一个非nil指针,并且能够取消引用该指针以进行读取,但是使用相同的指针进行写入会产生运行时错误。

unsafe.Pointer抱怨的是第6步,我认为是因为根据the package docs,

  

uintptr是整数,而不是引用。将Pointer转换为uintptr会创建一个没有指针的整数值   语义。即使uintptr保留了某个对象的地址,垃圾回收器也不会在对象移动时更新该uintptr的值,也不会阻止uintptr回收该对象。

更重要的是,据我所知(尽管我承认我无法在不扫描编译器和运行时源的情况下挖掘显式确认的麻烦),运行时似乎无法跟踪go vet类型作为具有自己的引用计数的离散指针;您当然可以将writing another interface value的两个词interface{}践踏成一整件事,但这似乎根本不是您想要做的(写到内存地址接口类型内部的指针的指针,全部不移动指针)。

有趣的是,我们似乎能够通过定义自己的结构化类型来approximate this behavior,而编译器没有给予特殊待遇(<接口> 显然(接口显然有些特殊,类型断言语法和所有)。也就是说,我们可以使用interface{}来维护指向内存中特定点的引用,无论我们将其强制转换为什么,即使值发生更改,内存地址也永远不会移动(并且值可以重新解释)通过将其投放到其他对象)。令我惊讶的是,至少在我自己的示例中,至少在Playground环境中,所指向的值似乎没有固定的大小。我们可以建立一个地址一次写入 ,并且即使有大量(或少量)数据也可以重复写入该地址。

当然,至少通过这种实现,我们会丢失与接口类型相关联的其他一些很好的东西,尤其是非空接口类型(即方法)。因此,没有办法使用它来(例如)制作超级鬼-的“泛型”类型。似乎接口是它自己的值,并且该值的定义的一部分是内存中的地址,但这与指针并不完全相同。