Go中的不安全指针:函数调用结束杀死数组

时间:2015-04-12 02:20:14

标签: arrays pointers go

我正在编写一个库,我希望将一个非特定类型的数组(或写入数组)返回给调用者。类型可以根据谁调用而变化 - 但是,我可以在我的函数中创建所述类型的所有对象。一种方法是调用者创建一个数组并且被调用者填充它 - 但是,没有办法知道这个数组将会有多长。 (有没有办法让被叫方使调用者的数组变大?请记住,被调用者只能看到x interface{} ...)

我选择的另一种方式,因为我看不出上面的可能性,是调用者给了我特定类型的指针,然后我将它重定向到我创建的对象数组。

以下是我的解决方案。我的问题:函数调用后为什么数组为空?它们在我的操作后指向同一个数组,它们应该是相同的。我忽略了什么吗?我想过GC,但它不会那么快,不是吗?

http://play.golang.org/p/oVoPx5Nf84

package main

import "unsafe"
import "reflect"
import "log"

func main() {
    var x []string
    log.Printf("before: %v, %p", x, x)
    manipulate(&x)
    log.Printf("after: %v, %p", x, x)
}

func manipulate(target interface{}) {
    new := make([]string, 0, 10)
    new = append(new, "Hello", "World")
    log.Printf("new: %v, %p", new, new)
    p := unsafe.Pointer(reflect.ValueOf(target).Pointer())
    ptr := unsafe.Pointer(reflect.ValueOf(new).Pointer())
    *(*unsafe.Pointer)(p) = ptr
}

1 个答案:

答案 0 :(得分:6)

首先,不安全通常是一个坏主意。反思也是如此,但不安全至少要差一个数量级。

以下是使用纯反射(http://play.golang.org/p/jTJ6Mhg8q9)的示例:

package main

import (
    "log"
    "reflect"
)

func main() {
    var x []string
    log.Printf("before: %v, %p", x, x)
    manipulate(&x)
    log.Printf("after: %v, %p", x, x)
}

func manipulate(target interface{}) {
    t := reflect.Indirect(reflect.ValueOf(target))
    t.Set(reflect.Append(t, reflect.ValueOf("Hello"), reflect.ValueOf("World")))
}

那么,为什么你的不安全方式不起作用?不安全是非常棘手的,有时需要了解内部。首先,你有一些误解:

  1. 您正在使用数组:您不是,您正在使用切片。切片是数组的视图。它们包含指向数据的指针,长度和容量。它们是内部结构而不是纯指针。

  2. 只有当指针是指针时,指针才返回指针:它实际上可以返回许多类型的值,如切片。 来自http://golang.org/pkg/reflect/#Value.Pointer

      

    如果v的Kind是Slice,则返回的指针指向切片的第一个元素。如果切片为零,则返回值为0.如果切片为空但非零则返回值为非零。

  3. 数组是指针:在Go中,数组实际上是值。这意味着它们在传递给其他函数或分配时被复制。这也意味着.Pointer方法不起作用。

  4. 您正在将指向数组的指针指定给切片类型。幸运的是,内部使用的切片的实现首先具有数据指针,因此您实际上设置了切片使用的内部数组指针。我必须强调,这实际上是纯粹的意外。即便如此,您还没有设置切片的长度和容量,因此它仍然会打印零元素。

    不安全可以让你做的事情水平很低,实际结果并没有真正定义。除非你真的知道自己在做什么,否则最好远离它。即便如此,请注意事情可以改变,今天有效的方法可能无法在下一版Go或其他实现中发挥作用。