登录
首页 >  Golang >  Go问答

Golang append() 方法的问题

来源:SegmentFault

时间:2023-02-19 17:05:17 216浏览 收藏

知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个Golang开发实战,手把手教大家学习《Golang append() 方法的问题》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!

问题内容

package main

import "fmt"

func main()  {
    x := make([]int,0,10)
    x = append(x, 1,2,3)
    y := append(x,4)
    z := append(x,5)
    fmt.Println(x)
    fmt.Println(y)
    fmt.Println(z)
}

为什么

y
输出的也是
[1 2 3 5]
呢?
append()
不是拷贝
x
的值么,那怎么
z
y
也覆盖了

正确答案

https://pkg.go.dev/builtin#ap...

The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:

简而言之,如果 append 的 slice 有足够容量,就不重新分配内存。只有容量不足的时候才会重新分配。

参考这篇 go 团队的博客 把 slices 说的很详细了。


重新审题。

你的问题是为什么

append(x,5)
之后
y
也跟着变了,要理解这一点必须理解 slice 类型是怎么回事。

上面链接里 Go 官方团队是这么说的:

It’s important to understand that even though a slice contains a pointer, it is itself a value. Under the covers, it is a struct value holding a pointer and a length. It is not a pointer to a struct.

slice 是一个包含指针的结构值,保存了指针和长度。 slice 是 而不是 引用 理解这一点很重要。

再看你的代码。

x = append(x, 1,2,3)
y = append(x, 4)
z = append(x, 5)

我们一句一句解读。

x = append(x, 1, 2, 3)

x
看作一个结构值
struct { underlying: *[cap]int, len: 0 }
,已知
cap
足够,不重新分配底层数组,这一句
append
调用做的事情就是:

arg = x 
// x 传入 append 时经历了一次复制
// arg 和 x 共享底层数组 

// append 的业务逻辑,增加长度,直接在底层数组上赋值
arg.underlying[x.len] = 1 
arg.len++
arg.underlying[x.len] = 2
arg.len++
arg.underlying[x.len] = 3
arg.len++ 

x = arg
// x.len = 3
// underlying = [1,2,3]

然后看这一句。

y = append(x, 4)

经过第一个

append
,现在的
x
struct{ underlying: [cap]int, len: 3}
cap
依然足够。所以
append
的实际行为如下。

arg = x
// x 传入 append 时经历了一次复制
// arg 和 x 共享底层数组 

// arg.len = 3
arg.underlying[arg.len] = 4
arg.len++
// arg.len = 4

y = arg
// y = struct { underlying: *[cap]int, len: 4}
// y.underlying = [1,2,3,4]

最后是

z = append(x, 5)

arg = x
// x 传入 append 时经历了一次复制
// arg 和 x 共享底层数组 

// arg.len = 3
// 所以修改的是共同的底层数组的第4个元素(下标从0开始数)
arg.underlying[arg.len] = 5
arg.len++

z = arg
// z.len=4
// z.underlying=[1,2,3,5]

因为

x
/
y
/
z
共享底层数组,
append
y
z
处调用时,根据
x
的长度,修改的都是第4个元素,造成执行
append(x, 5)
之后
y
的第4个元素也变成了5。

y = append(x, 4)
没有修改 x 的原因是,修改了但没有表现出来。因为
x.len
指明
x
的长度只有 3 ,虽然底层数组第 4 个元素已经被修改成了 4 ,但打印 x 的时候是不会打印出 4 的。

同理,

z = append(x, 5)
修改的底层数组是和
x
共享的,在执行了
append(x, 5)
之后其实
x
的底层数组第4个元素已经变成了5,只是
x
里保存的长度还是3,所以你没法看到它的第4个元素。但
y
z
共享底层数组,而且
y
的长度是4,所以你能看到
y
的第4个元素变成了5。

文中关于golang的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《Golang append() 方法的问题》文章吧,也可关注golang学习网公众号了解相关技术文章。

声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表