Go 切片内存分配

切片是数组的抽象。 切片使用数组作为底层结构。 切片包含三个组件:容量,长度和指向底层数组的指针。
通过使用 append 或 copy 函数可以增加切片的容量。 append 函数可以为数组的末尾增加值,并在需要时增加容量。

s :=[]string{}
//
for i:=0;i<10;i++ {
	s = append(s,"i")
	t.Log(len(s),cap(s))
}
    slice_test.go:25: 1 1
    slice_test.go:25: 2 2
    slice_test.go:25: 3 4
    slice_test.go:25: 4 4
    slice_test.go:25: 5 8
    slice_test.go:25: 6 8
    slice_test.go:25: 7 8
    slice_test.go:25: 8 8
    slice_test.go:25: 9 16
    slice_test.go:25: 10 16

当前储存空间存不下数据时,空间会以之前的空间数*2

比如:

	numbers := make([]int,5,10)
	numbers = append(numbers, 1,2,3,4,5)
	t.Log(numbers,cap(numbers))

定义 numbers 的数组长度为5 ,空间给他10,并追加数据到切片。

    slice_test.go:11: [0 0 0 0 0 1 2 3 4 5] 10

空间为10, 当我们再添加一个数据到切片时候,空间是多少了呢?

	numbers := make([]int,5,10)
	numbers = append(numbers, 1,2,3,4,5,6)
	t.Log(numbers,cap(numbers))
    slice_test.go:11: [0 0 0 0 0 1 2 3 4 5 6] 20

得到数据为20,和我们设想的一样。

注意

切片中的数据是共用内存,也就是我们选区切片区间的某一个值,并且在这个小对象中进行数据的修改,会影响整个切片的值,在开发当中非常要注意这种。

举个例子:

	numbers := make([]int,5,10)
	numbers = append(numbers, 1,2,3,4,5)
	t.Log(numbers,cap(numbers))

	first := numbers[5:7]
	second := numbers[6:8]
	t.Log(first,len(first),cap(first))
	t.Log(second,len(second),cap(second))

	second[0] = 9
	t.Log(first,len(first),cap(first))
	t.Log(second,len(second),cap(second))
	t.Log(numbers)

取了一个first 和second 这两个数据重合的数据是2.

打印一下:

slice_test.go:11: [0 0 0 0 0 1 2 3 4 5] 10
slice_test.go:15: [1 2] 2 5
slice_test.go:16: [2 3] 2 4
slice_test.go:19: [1 9] 2 5
slice_test.go:20: [9 3] 2 4
slice_test.go:21: [0 0 0 0 0 1 9 3 4 5]

虽然在second 中修改了数据,但是影响到了大对象numbers 的数据,还有一个要注意的是,first 小对象,他的内存分配是5,second 的空间是4,这是为啥呢?原来,这样取的小对象切片,的空间是这个切片加上大对象末尾的值

开发中如果修改了其中的一个变量,但是又不想影响大对象,这个时候会傻掉。

那如何去获取一个和numbers 一样的数据,但是又不会影响numbers呢?

我们copy一份numbers

	number2 := make([]int, len(numbers))
	// 将原始切片复制到新切片
	copy(number2, numbers)
	t.Log(number2,cap(numbers),cap(number2))

	number2[0] = 9

	t.Log(number2,numbers,cap(numbers),cap(number2))

输出一下结果:

slice_test.go:11: [0 0 0 0 0 1 2 3 4 5] 10
slice_test.go:15: [1 2] 2 5
slice_test.go:16: [2 3] 2 4
slice_test.go:19: [1 9] 2 5
slice_test.go:20: [9 3] 2 4
slice_test.go:21: [0 0 0 0 0 1 9 3 4 5]
slice_test.go:25: [0 0 0 0 0 1 9 3 4 5] 10 10
slice_test.go:29: [9 0 0 0 0 1 9 3 4 5] [0 0 0 0 0 1 9 3 4 5]10 10

好,不懂在座的各位看懂了没有。没有影响的,通过copy 切片再去修改切片的值,是不会印象原切片的。

关于切片的知识,还远远不止这些,后续遇到问题,再补充吧,欢迎关注

### 回答1: Go 语言中的 append 函数用于将一个或多个元素附加到切片的末尾。append 切片分配新的内存空间,并复制旧切片的数据,将新元素添加到新的内存空间中。而 append 元素则只是将元素添加到切片的原有内存空间中。 ### 回答2: 在Go语言中,使用`append`函数来添加元素到一个切片中。无论是向一个切片中`append`元素,还是向一个切片追加另一个切片,它们在内存分配上有些许区别。 当向切片中`append`一个元素时,如果切片的容量足够容纳新的元素,那么会直接将元素添加在切片的末尾。这意味着元素是直接被追加到原切片的内存区域上的,不会重新分配内存。如果切片的容量不足以容纳新元素,那么Go语言会为切片分配一块新的内存区域,并将原切片中的元素和新的元素都复制到这块新内存区域上,并返回一个指向新内存区域的切片。 当向切片中`append`另一个切片时,Go语言首先会比较目标切片的容量和要追加切片的长度。如果目标切片的容量足够容纳要追加切片的元素,那么会直接将要追加切片的元素复制到目标切片的内存区域上,并返回一个指向目标切片切片。这种情况下,不会进行内存的重新分配和复制。如果目标切片的容量不足以容纳要追加切片的元素,那么Go语言会为目标切片和要追加切片分配一块新的内存区域,并将两个切片的元素都复制到这块新内存区域上,并返回一个指向新内存区域的切片。 总结起来,当向切片`append`元素或另一个切片时,如果切片的容量足够,不会重新分配内存;如果容量不足,将会重新分配内存并复制元素。 ### 回答3: 在Go语言中,使用`append`函数可以向切片中追加元素。在进行`append`操作时,会涉及到内存分配的问题。 首先,切片和数组的区别在于切片是一个引用类型,它的底层指向一个数组。切片本身并不存储元素,而是存储了一个指向底层数组的指针、切片的长度和容量。当切片的容量不足以容纳新的元素时,就需要进行内存扩容操作。 对于切片的`append`操作,当切片的容量不足时,会创建一个新的底层数组,并将原来的元素复制到新的底层数组中。新的底层数组的容量通常会是原来的两倍,并且会根据实际情况进行调整。然后,将新的元素追加到新的底层数组中,并更新切片的指针、长度和容量。 值得注意的是,由于切片本身是一个指向底层数组的指针,并不需要像数组那样重新赋值给一个新的变量。因此,对切片进行`append`操作并赋值给切片本身,会在原有的切片上进行操作,而不是创建一个新的切片。 一般情况下,`append`操作的时间复杂度为O(1),但是当底层数组发生扩容时,时间复杂度会升至O(n)。此外,由于底层数组的复制操作会涉及到内存分配和数据拷贝,`append`操作也会产生额外的内存开销。考虑到性能方面的原因,如果预先知道切片的容量大小,可以使用`make`函数初始化切片并指定容量,以减少切片的扩容次数,从而提高性能。 综上所述,golang的`append`切片和元素的内存分配区别在于:`append`切片会根据实际情况进行底层数组的内存扩容,并重新分配内存,同时对切片本身的指针、长度和容量进行更新;而`append`元素只需要将新的元素追加到底层数组的末尾,并不涉及内存扩容操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

廖圣平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值