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条)

报告相同问题?

悬赏问题

  • ¥60 ESP32怎么烧录自启动程序
  • ¥50 html2canvas超出滚动条不显示
  • ¥15 java业务性能问题求解(sql,业务设计相关)
  • ¥15 52810 尾椎c三个a 写蓝牙地址
  • ¥15 elmos524.33 eeprom的读写问题
  • ¥15 使用Java milo连接Kepserver服务端报错?
  • ¥15 用ADS设计一款的射频功率放大器
  • ¥15 怎么求交点连线的理论解?
  • ¥20 软件开发方法学习来了
  • ¥15 微信小程序商城如何实现多商户收款 平台分润抽成