如果len不变,为什么数组/切片的指针在Go中也会变化?

时间:2018-07-18 13:38:38

标签: go

我制作了一个程序,以了解有关数组和切片的更多信息,在此示例中,我只是更改了固定位置的值。当我查看指针地址时,每次更改后都会更改。为什么会这样?

来源:

func main() {
    tstSlice2()

}

func tstSlice2() {
    var meuSlice = make([]int, 1, 2)
    meuSlice[0] = 1
    printSlice(meuSlice, "meuSlice")
    meuSlice[0] = 2
    printSlice(meuSlice, "meuSlice")
    meuSlice[0] = 3
    printSlice(meuSlice, "meuSlice")
}

func printSlice(meuSlice []int, sliceName string) {
    fmt.Printf("%v> %v - %v - %v - %p \n", sliceName, len(meuSlice), cap(meuSlice), meuSlice, &meuSlice)
}

输出:

▶ go run .\src\array_e_slices\slice.go    
meuSlice> 1 - 2 - [1] - 0xc0420503e0  
meuSlice> 1 - 2 - [2] - 0xc042050440  
meuSlice> 1 - 2 - [3] - 0xc042050480 

3 个答案:

答案 0 :(得分:1)

因为您每次都要传递切片的副本。未复制基础数组时,切片会复制。我认为原则上您可以将slice视为结构:

type Slice struct {
   pointer PointerToArray
   StartIndex int
   Len int
}

下面的代码应打印相同的指针:

func tstSlice2() {
    var meuSlice = make([]int, 1, 2)
    meuSlice[0] = 1
    printSlice(&meuSlice, "meuSlice")
    meuSlice[0] = 2
    printSlice(&meuSlice, "meuSlice")
    meuSlice[0] = 3
    printSlice(&meuSlice, "meuSlice")
}

func printSlice(meuSlice *[]int, sliceName string) {
    fmt.Printf("%v> %v - %v - %v - %p \n", sliceName, len(*meuSlice), cap(*meuSlice), *meuSlice, meuSlice)
}

答案 1 :(得分:1)

您在printSlice函数中传递的不是切片,而是切片标头,它描述了基础数组。当您将切片传递给函数时,将生成标头的副本,因为在运行中元素总是 passed by value

  

在函数调用中,函数值和参数在   通常的顺序。评估之后,调用的参数   通过值传递给函数,被调用函数开始   执行。函数的返回参数按值传递   函数返回时返回到调用函数。

如果要检查此标头中的内容,请检查reflect.SliceHeader的类型。

代码示例:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    tstSlice2()

}

func tstSlice2() {
    var meuSlice = make([]int, 1, 2)
    meuSlice[0] = 1
    printSlice(meuSlice, "meuSlice")
    meuSlice[0] = 2
    printSlice(meuSlice, "meuSlice")
    meuSlice[0] = 3
    printSlice(meuSlice, "meuSlice")
}

func printSlice(meuSlice []int, sliceName string) {
    hdr := (*reflect.SliceHeader)(unsafe.Pointer(&meuSlice))
    fmt.Printf("%v> %v - %v - %v - %p - array: %v\n", sliceName, len(meuSlice), cap(meuSlice), meuSlice, &meuSlice, hdr.Data)

}

输出:

meuSlice> 1 - 2 - [1] - 0xc42000a060 - array: 842350559504
meuSlice> 1 - 2 - [2] - 0xc42000a0a0 - array: 842350559504
meuSlice> 1 - 2 - [3] - 0xc42000a0e0 - array: 842350559504

去游乐场: https://play.golang.org/p/x2OoIHgU9St

您可以在博客文章Go Slices: usage and internals(尤其是Slice内部构件部分)中找到更多信息

  

切片是数组段的描述符。它由一个指针组成   到数组,段的长度及其容量(最大   段的长度。

Slice

传递给函数的是此结构的值,而不是基础数组本身。为此,您需要使用*[]int参数,如其他答案中所述。

答案 2 :(得分:0)

切片值包含一个指向后备数组,长度和容量的指针。有关所有详细信息,请参见Go Slices: usage and internals

程序将打印切片menuSlice的地址。每次调用的参数地址都不相同。

看来您的目标是打印切片的后备阵列的地址。为此,请打印第一个slice元素的地址。第一个元素的地址与整个后备阵列的地址相同。

func printSlice(meuSlice []int, sliceName string) {
    fmt.Printf("%v> %v - %v - %v - %p \n", sliceName, len(meuSlice), cap(meuSlice), meuSlice, &meuSlice[0])
}

该程序的输出为:

meuSlice> 1 - 2 - [1] - 0x10414020 
meuSlice> 1 - 2 - [2] - 0x10414020 
meuSlice> 1 - 2 - [3] - 0x10414020 

playground example

上面的代码对nil和零长度切片感到恐慌。为了避免出现紧急情况,请在获取第一个元素的地址之前检查切片的长度:

func printSlice(meuSlice []int, sliceName string) {
    var p *int
    if len(meuSlice) > 0 {
        p = &meuSlice[0]
    }
    fmt.Printf("%v> %v - %v - %v - %p \n", sliceName, len(meuSlice), cap(meuSlice), meuSlice, p)
}

playground example