Golang中append()函数的内存分配机制探讨

Golang中append()函数的内存分配机制探讨 我对切片追加时的内存分配机制有些困惑。我写了以下示例程序:

package main

import "fmt"

func main() {
	var a []int
	a = append(a, 3)
	fmt.Println(&a[0])
	a = append(a, 5)
	fmt.Println(&a[0])
}

我看到的输出是:

0xc0000b6018
0xc0000b6030

我不明白为什么在这两种情况下 a[0] 的地址会不同。如果有人能解释这个现象,我将不胜感激。

2 回复

每次使用内置的 cap 函数来获取切片的容量。容量是切片所指向的已分配数组的总长度。第一次调用 append 时,发现 anil,因此它会分配一个容量为1的切片来容纳这一个元素。你下一次调用 append 时,发现没有足够的容量来容纳下一个元素,于是它会分配另一个底层数组,其容量是第一个数组的两倍(直到容量变得非常大时,它会转而线性增长),将第一个数组的元素复制到新数组中,然后进行追加。如果你使用 make([]int, 0, 2) 来初始化 a,那么底层数组将以正确的容量开始,并且在追加前两个元素后,a[0] 的地址不会改变。

func main() {
    fmt.Println("hello world")
}

更多关于Golang中append()函数的内存分配机制探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,切片追加时的内存分配机制涉及到底层数组的重新分配。当切片的容量不足以容纳新元素时,append()会创建一个新的底层数组,将原有数据复制到新数组,然后追加新元素。这会导致底层数组地址的改变。

在你的示例中:

  1. 初始切片a为零值nil,没有分配底层数组。
  2. 第一次append(a, 3)时,分配一个新的底层数组,容量通常为1(具体实现可能不同),a[0]的地址为0xc0000b6018
  3. 第二次append(a, 5)时,由于容量不足(当前容量为1,需要容量为2),触发重新分配。新数组的容量通常为原容量的两倍(即2),原有数据(元素3)被复制到新数组,然后追加元素5。新底层数组的地址变为0xc0000b6030,因此a[0]的地址改变。

以下代码演示了容量变化对地址的影响:

package main

import "fmt"

func main() {
	var a []int
	fmt.Printf("初始: len=%d, cap=%d\n", len(a), cap(a))

	a = append(a, 3)
	fmt.Printf("第一次追加后: len=%d, cap=%d, &a[0]=%p\n", len(a), cap(a), &a[0])

	a = append(a, 5)
	fmt.Printf("第二次追加后: len=%d, cap=%d, &a[0]=%p\n", len(a), cap(a), &a[0])

	// 追加到容量足够时,地址不变
	a = append(a, 7)
	fmt.Printf("第三次追加后: len=%d, cap=%d, &a[0]=%p\n", len(a), cap(a), &a[0])
}

输出示例:

初始: len=0, cap=0
第一次追加后: len=1, cap=1, &a[0]=0xc0000b6018
第二次追加后: len=2, cap=2, &a[0]=0xc0000b6030
第三次追加后: len=3, cap=4, &a[0]=0xc0000b6030

可以看到,第三次追加时容量足够(容量2→4),底层数组地址未变。这种机制平衡了内存使用和性能,但需要注意地址变化可能导致的数据引用问题。

回到顶部