Arrays Go中的不安全指针:函数调用结束终止数组

Arrays Go中的不安全指针:函数调用结束终止数组,arrays,pointers,go,Arrays,Pointers,Go,我正在编写一个库,我想向调用者返回一个非特定类型的数组(或写入数组)。类型可能会有所不同,这取决于调用的对象-但是,我可以在我的函数中创建尽可能多的上述类型的对象。一种方法是调用者创建一个数组,被调用者填充该数组-但是,没有办法知道该数组的长度。(被调用者有没有办法使调用者的数组变大?记住,被调用者只看到x接口{}。) 我选择的另一种方式是,调用方给我他特定类型的指针,我将其重定向到我创建的对象数组,因为我不知道上面是怎么回事 下面是我的解决方案。我的问题:为什么函数调用后数组是空的?在我的操作

我正在编写一个库,我想向调用者返回一个非特定类型的数组(或写入数组)。类型可能会有所不同,这取决于调用的对象-但是,我可以在我的函数中创建尽可能多的上述类型的对象。一种方法是调用者创建一个数组,被调用者填充该数组-但是,没有办法知道该数组的长度。(被调用者有没有办法使调用者的数组变大?记住,被调用者只看到
x接口{}
。)

我选择的另一种方式是,调用方给我他特定类型的指针,我将其重定向到我创建的对象数组,因为我不知道上面是怎么回事

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


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

下面是使用纯反射()的示例:


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

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

  • 指针仅当它是指针时才返回指针:它实际上可以为许多类型(如切片)返回值。 发件人:

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

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

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

    “不安全”允许您以如此低的级别进行操作,以至于无法真正定义实际结果。最好远离它,除非你真的知道自己在做什么。即使如此,也要意识到事情可能会发生变化,而今天起作用的东西在下一版本的Go或其他实现中可能不起作用

    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
    }
    
    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")))
    }