指向“slice”之后`slice`元素的指针的行为已附加到

时间:2015-07-26 03:24:40

标签: pointers go slice

我想知道在追加slice之后指向slice元素的指针的行为是什么,例如:

package main

import "fmt"

func main() {
    my_slice := []int {3}

    silly_ptr := &my_slice[0]
    // Do we know that silly_ptr points to value equal 3
    // all the time? (If we don't explicitly change it).

    fmt.Printf("%p\n", silly_ptr)
    fmt.Println(*silly_ptr)

    for i := 0; i < 10; i++ {
        my_slice = append(my_slice, i)
    }

    silly_ptr_2 := &my_slice[0]

    fmt.Printf("%p\n", silly_ptr_2)
    fmt.Println(*silly_ptr_2)
}

产生:(没有惊喜)

0xc20800a200
3
0xc20805a000
3

我知道当附加到动态数组时,在某些点我们重新填充整个数组,因此原始数组元素的内存地址不可靠。据我所知,类似的代码在c++中有效,但silly_ptr可能指向任何内容。 rust如果被借用,则不允许变异vector,因此上述逻辑无法编译。

但是Go呢?我知道通过escape analysis返回指向局部变量的指针是有效的,该变量将在堆上为您创建。我的直觉告诉我,同样的逻辑适用于上述情况。 silly_ptr指向的内存位置不会重新填充,因此将始终存储3(如果我们不明确地更改它)。这是对的吗?

1 个答案:

答案 0 :(得分:1)

不,它不会总是存储3。

Go有内存管理。只要有一个活动指针指向切片的底层数组,底层数组就会被固定,它不会被垃圾回收。如果您有指向基础数组元素的指针,则可以更改该元素的值。例如,

package main

import (
    "fmt"
)

func pin() *int {
    s := []int{3}
    fmt.Println(&s[0])
    a := &s[0]
    s = append(s, 7)
    fmt.Println(&s[0])
    return a
}

func main() {
    a := pin()
    fmt.Println(a, *a)
    *a = 42
    fmt.Println(a, *a)
}

输出:

0xc82000a340
0xc82000a360
0xc82000a340 3
0xc82000a340 42

切片描述符包含指向底层数组的指针,因此您可以看到与切片类似的内容。例如,

package main

import (
    "fmt"
)

func pin() []int {
    s := []int{3}
    fmt.Println(&s[0])
    d := s
    s = append(s, 7)
    fmt.Println(&s[0])
    return d
}

func main() {
    d := pin()
    fmt.Println(&d[0], d)
    d[0] = 42
    fmt.Println(&d[0], d)
}

输出:

0xc82000a340
0xc82000a360
0xc82000a340 [3]
0xc82000a340 [42]