我发现我的代码有不同的结果如下,这是一个指针,指的是Go.Tour编译器(http://tour.golang.org/welcome/1)和我的本地编译器(Go 1.4版)之间的幻灯片
哪一个是正确的?我也想知道我的代码p1,p2之间的指针是如何工作的? 因为地址似乎没有移动,但p1使用引用但p2使用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 \n", a[0], &a[0], *p)
a[0] = 2
fmt.Printf("a[0] = %d pointer=%d, p = %d \n", 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\n", c[0], &c[0], *p2)
c = append(c, 1)
c[0] = 2
fmt.Printf("c[0]=%d pointer=%d, p2 = %d\n", 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??
*/
}
更新: 我使用指针切片的原因是我试图测试向量push_pack问题,它在Go的Web端存在。请参阅http://doc.rust-lang.org/nightly/intro.html#ownership。
答案 0 :(得分:5)
首先,对于切片的任何内容,我建议您阅读Go Slices: usage and internals。简短的故事是Go对切片容量的处理可能会让人感到不安。
给定的切片变量有三个组件:指向数据数组的基础指针,长度和容量。关于差异的大量词汇已经泄露,但这里的重要部分是长度(有效地)是底层内存缓冲区的当前使用部分,而容量是底层缓冲区的总体大小。这是一个不精确的定义,但它在实践中运作良好。
神秘的下一部分是append
内置。有时候附加功能实际上有点难以理解,可能是Go中最大的陷阱之一:
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\n", c[0], &c[0], *p2)
c = append(c, 1)
c[0] = 2
fmt.Printf("c[0]=%d pointer=%d, p2 = %d\n", c[0], &c[0], *p2)
c = append(c, 1)
c[0] = 25
fmt.Printf("c[0]=%d pointer=%d, p2 = %d\n", c[0], &c[0], *p2)
你会得到c[0]=25, p2=2
。我们只添加了一个语句,突然指针和切片值发散了!
这意味着更改了上限,或者更确切地说,使用了新的缓冲区。实际上,在第一次追加后,但在第三次追加之前打印cap(c)
将产生2
。这意味着当将单个元素附加到容量0的切片时,Go会将[脚注]初始化为长度为1且容量 2 的切片。因此,在第二次追加后没有分配新的缓冲区,因为它有空间。这就是为什么p2
和c[0]
在第二次附加后相同而不是第三次附加的原因。
一般而言,虽然切片和对内存中特定位置的引用的确切规则是一致的,但实际上切片增长行为非常精巧,通常最好不要依赖指针切片值(或者两个切片变量具有相同的底层缓冲区),除非您计划从不使用追加,或者使用make
预先分配缓冲区,使用append永远不会重新分配。
[脚注]不完全正确,我想发出一个巨大的警告附加后的确切容量依赖于实现。请不要依赖附件在编译器或甚至不同编译器目标之间的相容能力
答案 1 :(得分:1)
要在两个环境(play.golang.org和local go 1.4)中获得相同的结果,您需要添加:
c[0] = 2
p2 = &c[0] // REDEFINE p2 there
(如this example)
当在本地运行时,这将给出:
c[0]=0 pointer=826814759400, p2 = 0
c[0]=2 pointer=826814759440, p2 = 2
而不是:
c[0]=0 pointer=826814759400, p2 = 0
c[0]=2 pointer=826814759440, p2 = 0
正如“Inside the Go Playground”中所提到的那样,由于“Playground程序的CPU时间和内存可以使用的数量有限”,因此切片的内存分配在操场之间的管理方式不同和完整的Go计划。
如果您要删除c = append(c, 1)
,您将获得预期的结果。
使用本地程序,扩展切片results in a new slice:其默认容量为1,添加新元素会创建一个具有新容量的新切片(请参阅“slice internals”)。 不适用于默认情况下切片容量可能较大的操场。