首页 > 文章列表 > Go语言中,当切片在内存中被移动时,其元素指针会有何变化?

Go语言中,当切片在内存中被移动时,其元素指针会有何变化?

179 2024-04-24
问题内容

我有以下代码

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)仍然引用它,它就仍然可用。一旦对该内存的所有引用都被删除(即超出范围),垃圾收集器可能会允许它被其他东西使用。