我想知道在追加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
(如果我们不明确地更改它)。这是对的吗?
答案 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]