doujing5150 2014-12-27 08:38
浏览 37
已采纳

指向切片的指针是使用引用还是复制?

I found there is different result from my code as follow which is a pointer refer to a slide between Go.Tour compiler (http://tour.golang.org/welcome/1) and my local compiler (Go Version 1.4)

Which one is correct? And I am also wondering how pointer work between my code p1, p2? Because the address seems not moving but p1 using reference but p2 using copy.

package main

import "fmt"

func main() {
    var a []int
    var b []int
    a = append(a, 0)
    b = append(b, 0)
    p := &a[0]
    fmt.Printf("a[0] = %d pointer=%d, p = %d 
", a[0], &a[0], *p)
    a[0] = 2
    fmt.Printf("a[0] = %d pointer=%d, p = %d 
", a[0], &a[0], *p)
    /*
        a[0] = 0, p = 0
        a[0] = 2, p = 2
    */
    var c []int
    var d []int
    c = append(c, 0)
    d = append(d, 0)
    p2 := &c[0]
    fmt.Printf("c[0]=%d pointer=%d, p2 = %d
", c[0], &c[0], *p2)
    c = append(c, 1)
    c[0] = 2
    fmt.Printf("c[0]=%d pointer=%d, p2 = %d
", c[0], &c[0], *p2)
    /* 
        c[0]=0, p2 = 0
        c[0]=2, p2 = 0

      copy the same code run in http://tour.golang.org/welcome/1 will get.
        c[0]=0, p2 = 0
        c[0]=2, p2 = *2*  << why??

    */
}

Update: The reason why I use pointer to slice is that I am trying to test vector push_pack issue which RUST present on their web side in Go. Refer to http://doc.rust-lang.org/nightly/intro.html#ownership.

  • 写回答

2条回答 默认 最新

  • douzhang1955 2014-12-27 09:12
    关注

    Firstly, as for anything with slices, I'd like to recommend reading through Go Slices: usage and internals. The short story is Go's handling of a slice's capacity with append can be wonky.

    A given slice variable has three components: an underlying pointer to a data array, a length, and a capacity. Plenty of words have been spilled about the difference, but the important part here is that the length is (effectively) the currently used part of the underlying memory buffer, and the capacity is the overall size of the underlying buffer. This is an imprecise definition, but it works well enough in practice.

    The next part of the mystery is the append builtin. The functionality of append is actually somewhat hard to reason about sometimes, and probably one of the biggest gotchas in Go:

    1. If the underlying buffer is large enough (cap > len), simply increase len by the number of elements to be added and put the data in the new space.
    2. If the underlying buffer is NOT large enough, allocate a new buffer with some larger capacity, copy the old buffer into the new buffer, and add all the new elements.

    The biggest sticking point with 2 is that given two arbitrary operations on the same slice after an append, it's difficult to know if the old or a new memory buffer is being effected a priori. Indeed, let's try this:

    var c []int
    var d []int
    c = append(c, 0)
    d = append(d, 0)
    p2 := &c[0]
    fmt.Printf("c[0]=%d pointer=%d, p2 = %d
    ", c[0], &c[0], *p2)
    c = append(c, 1)
    c[0] = 2
    fmt.Printf("c[0]=%d pointer=%d, p2 = %d
    ", c[0], &c[0], *p2)
    c = append(c, 1)
    c[0] = 25
    fmt.Printf("c[0]=%d pointer=%d, p2 = %d
    ", c[0], &c[0], *p2)
    

    playground

    You'll get c[0]=25, p2=2. We've only added one more statement, and suddenly the pointer and slice values are diverging!

    This means that the cap changed, or rather, a new buffer was used. Indeed, printing cap(c) after the first append, but before the third, will yield 2. This means that when appending a single element to a slice of capacity 0, Go initializes[footnote] a slice of length 1 and capacity 2. So no new buffer is allocated after the second append, because there's space. That's why p2 and c[0] are the same after the second append but not the third.

    In general, while the exact rules for when a slice and a reference to a specific location in memory are consistent, in practice the slice-growing behavior is so finnicky that it's usually best to never rely on pointers to slice values (or two slice variables having the same underlying buffer) unless you plan on either never using append, or pre-allocating the buffer with make to such a size that using append will never reallocate.

    [footnote] Not entirely true, I'd like to give a huge warning the exact capacity after append is implementation dependent. PLEASE DO NOT RELY ON APPEND'S RESULTING CAPACITY BEING CONSISTENT BETWEEN COMPILERS OR EVEN DIFFERENT COMPILER TARGETS

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能
  • ¥15 jmeter脚本回放有的是对的有的是错的
  • ¥15 r语言蛋白组学相关问题
  • ¥15 Python时间序列如何拟合疏系数模型
  • ¥15 求学软件的前人们指明方向🥺