我有以下代码
package main import "fmt" func main() { a := []int{1} b := &a[0] fmt.Println(a, &a[0], b, *b) // prints [1] 0xc00001c030 0xc00001c030 1 a = append(a, 1, 2, 3) fmt.Println(a, &a[0], b, *b) // prints [1 1 2 3] 0xc000100020 0xc00001c030 1 }
首先它创建一个 1 int 的切片。它的 len 是 1,cap 也是 1。然后我获取一个指向它的第一个元素的指针,并在 print 中获取底层指针值。正如预期的那样,它工作得很好。
然后我向切片中添加了 3 个元素,从而扩展了切片的容量,从而将其复制到内存中的另一个位置。之后,我打印切片第一个元素的地址(通过获取指针),该地址现在与 b
中存储的地址不同。
但是,当我打印 b
的基础值时,它也可以正常工作。我不明白为什么它有效。据我所知,第一个元素 b
指向的切片已复制到内存中的另一个位置,因此它之前的内存肯定已被释放。不过,它似乎仍然存在。
如果我们查看映射,golang 甚至不允许我们通过键在元素上创建指针,因为完全相同的问题 - 底层数据可以移动到内存中的另一个位置。然而,它对于切片来说效果非常好。为什么会这样呢?它到底是如何运作的?内存没有被释放是因为仍然有一个变量指向该内存吗?它与地图有何不同?
当 Go 将切片移动到内存中的另一个位置时,指向元素的指针会发生什么?
什么都没有。
[W]当我打印 b
的基础值时,它也可以正常工作。我不明白为什么它会起作用。
为什么不起作用?
最初指向的内存位置仍然存在,没有改变。只要任何东西(例如 b
)仍然引用它,它就仍然可用。一旦对该内存的所有引用都被删除(即超出范围),垃圾收集器可能会允许它被其他东西使用。