【问题标题】:Go: Passing pointers of an array to gob without copying?Go:将数组的指针传递给 gob 而不复制?
【发布时间】:2014-08-03 05:47:32
【问题描述】:

我有一个非常非常大的地图数组(不是切片),然后我正在尝试对其进行编码。我确实需要避免制作数组的副本,但我不知道该怎么做。

到目前为止,我有这个:

func doSomething() {
 var mygiantvar [5]map[string]Searcher
 mygiantvar = Load()
 Save(`file.gob.gz`, &mygiantvar)
}

func Save(filename string, variable *[5]map[string]Searcher) error {
    // Open file for writing
    fi, err := os.Create(filename)
    if err !=nil {
        return err
    }
    defer fi.Close()
    // Attach gzip writer
    fz := gzip.NewWriter(fi)
    defer fz.Close()
    // Push from the gob encoder
    encoder := gob.NewEncoder(fz)
    err = encoder.Encode(*variable)
    if err !=nil {
        return err
    }
    return nil
}

据我了解,这会将 mygiantvar 的指针传递给 Save,它会保存第一个副本。但是整个数组肯定会被复制到 encoder.Encode 中,然后它会复制更多的函数,对吧?

这个mygiantvar 变量的大小大约为 10GB。所以它必须避免被复制。

但话又说回来,也许只有实际的数组 [5] 部分被复制,但其中的映射是数组内的指针,因此将复制指向映射的指针数组而不是映射本身?我对此一无所知 - 这一切都非常令人困惑。

有什么想法吗?

【问题讨论】:

    标签: pointers go


    【解决方案1】:

    请注意,Encoder.Encode 将传递 interface{}

    func (enc *Encoder) Encode(v interface{}) error {
    

    这意味着一种指向您将传递给它的任何内容的指针,正如我在“what is the meaning of interface{} in golang?”中描述的那样
    (另见“Why can't I assign a *Struct to an *Interface?”)

    接口值不是具体结构的值(因为它具有可变大小,这是不可能的),但它是一种指针(更准确地说是指向结构的指针和指针类型)

    这意味着它不会复制您地图的全部内容(或此处your array)。
    由于数组是一个值,您可以对其进行切片以避免在调用Encode() 期间出现任何复制:

    err = encoder.Encode(*variable[:])
    

    见“Go Slices: usage and internals

    这也是给定数组创建切片的语法:

    x := [3]string{"Лайка", "Белка", "Стрелка"}
    s := x[:] // a slice referencing the storage of x
    

    如果这不起作用,您可以保留*variable(这里是一个数组:[5]map[string]Searcher),因为映射类型是引用类型,如指针或切片:副本不会很大。
    见“Go maps in action”。

    虽然数组在传递给interface{} 时会被复制,但地图内容不会被复制。
    请参阅此 play.golang.org 示例:

    package main
    
    import "fmt"
    
    func main() {
        var a [1]map[string]int
        a[0] = make(map[string]int)
        a[0]["test"] = 0
        modify(a)
        fmt.Println(a)
    }
    
    func modify(arr interface{}) {
        a := arr.([1]map[string]int)
        a[0]["test"] = -1
    }
    

    输出:

    [map[test:-1]]
    

    【讨论】:

    • 但是数组本身被复制到了encoder.Encode(*variable)?
    • @Alasdair 是的,但您可以将其切片以避免复制:*variable[:]
    • 但是如果我将它切片,那么它将是不同的类型,并且肯定不会加载回将自身定义为[5]map[string]Searcher的结构中?我希望即使复制了数组,其中的映射也不会被复制,只是复制了它们的指针。
    • @Alasdair 你是对的,我已经编辑了答案:地图内容不会被复制。只有地图本身,它是一个引用类型。
    • 接口可能包含一个指针,但它是一个指向包含值本身的新分配内存的指针,并且该内存是通过复制值来初始化的。因此,在他的数组情况下,它会为数组的大小分配空间,复制内容,然后在接口中放置一个指向新内存的指针。要证明接口不只是通过引用传递,请参阅example
    猜你喜欢
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 2021-11-02
    • 2021-06-13
    • 1970-01-01
    • 2019-03-07
    • 2013-02-15
    • 1970-01-01
    相关资源
    最近更新 更多