douchi1945 2015-04-12 02:20
浏览 34
已采纳

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

I'm writing a library and I want to return an array (or write to an array) of an unspecific type to the caller. The type can vary, depending on who calls - I can, however, create as many objects of said type from within my function. One way would be that the caller creates an array and the callee fills that - however, there is no way of telling how long this array is going to be. (Is there a way that the callee makes the caller's array bigger? Remember, the callee only sees x interface{}...)

The other way which I chose because I don't see how above is possible, is that the caller gives me the pointer of his specific type and I redirect it to the array of objects which I created.

Below is my solution. My question: why is the array empty after the function call? They are pointing to the same array after my operation, they should be the same. Am I overlooking something? I thought about GC, but it couldn't be that fast, could it?

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条回答 默认 最新

  • duanquezhan7268 2015-04-12 02:36
    关注

    First of all, unsafe is usually a bad idea. So is reflection, but unsafe is at least an order of magnitude worse.

    Here is your example using pure reflection (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")))
    }
    

    So, why didn't your unsafe way work? Unsafe is extremely tricky and at times requires understanding the internals. First, some misconceptions you have:

    1. You are using arrays: you are not, you are using slices. Slices are a view of an array. They contain within them a pointer to the data, a length, and a capacity. They are stucts internally and not pure pointers.

    2. Pointer returns the pointer only if it is a pointer: it can actually return a value for many types like a slice. From http://golang.org/pkg/reflect/#Value.Pointer:

      If v's Kind is Slice, the returned pointer is to the first element of the slice. If the slice is nil the returned value is 0. If the slice is empty but non-nil the return value is non-zero.

    3. Arrays are pointers: in Go, arrays are actually values. That means they are copied when passed to other functions or assigned. It also means the .Pointer method wouldn't work.

    You are assigning a pointer to an array to a slice type. By luck, the implementation of slices used internally has the data pointer first so you are actually setting the internal array pointer used by the slice. I must stress that is is effectively pure accident. Even still, you are not setting the length and capacity of the slice so it still prints zero elements.

    Unsafe lets you do things at such a low level that the actual results aren't really defined. It is best to stay away from it unless you really know what you are doing. Even then, be aware that things can can change and what works today may not work in the next version of Go or another implementation.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 file converter 转换格式失败 报错 Error marking filters as finished,如何解决?
  • ¥15 ubuntu系统下挂载磁盘上执行./提示权限不够
  • ¥15 Arcgis相交分析无法绘制一个或多个图形
  • ¥15 关于#r语言#的问题:差异分析前数据准备,报错Error in data[, sampleName1] : subscript out of bounds请问怎么解决呀以下是全部代码:
  • ¥15 seatunnel-web使用SQL组件时候后台报错,无法找到表格
  • ¥15 fpga自动售货机数码管(相关搜索:数字时钟)
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型