Skip to content

slice 的问题 #124

Description

@codcodog

slice 的问题

场景

package main
import"fmt"typecities []stringfuncmain() {
Nepal:=cities{"Kathmandu", "Pokhara", "Lumbini"}
Nepal.add()
Nepal.print()
}
func (ccities) print() {
fori, city:=rangec {
fmt.Println(i, city)
}
}
func (ccities) add() {
c=append(c, "hello")
}

执行输出如下:

0 Kathmandu
1 Pokhara
2 Lumbini

结果没有 3 hello

原因

slicearray 很类似,但是它更灵活,可以自动扩容.

由源码可以看到它的本质:

// runtime/slice.go
type slice struct {
array unsafe.Pointer // 元素指针
len int // 长度
cap int // 容量
}

slice 有三个属性,分别是指向底层数组的指针,切片可用的元素个数,底层数组的元素个数.

底层的数组可能被多个 slice 指向,所以修改其中一个 slice 的时候,会因此影响到其他的 slice.

上面的代码 c = append(c, "hello") 之后,遍历 c 却没有 hello,这主要涉及到 slice 的扩容问题.

在添加元素的时候,如果 slice 的容量不够的时候,是会先读取原有元素,然后 cap 扩大一倍,再重新复制到新的数组中去,slice 的数组指针也更新为新的数组指针.

把上面代码 c 的指针地址打印下,就清晰很多了.

package main
import"fmt"typecities []stringfuncmain() {
Nepal:=cities{"Kathmandu", "Pokhara", "Lumbini"}
Nepal.add()
Nepal.print()
}
func (ccities) print() {
fmt.Printf("print(): %p \n", c)
fori, city:=rangec {
fmt.Println(i, city)
}
}
func (ccities) add() {
fmt.Printf("add() append before: %p \n", c)
c=append(c, "hello")
fmt.Printf("add() append after: %p \n", c)
}

输出如下:

add() append before: 0xc000070150 # append 前后地址不一样
add() append after: 0xc00007c120
print(): 0xc000070150
0 Kathmandu
1 Pokhara
2 Lumbini

解决

add() 方法的时候,使用指针赋值,直接修改原来的 slice.

func (c*cities) add() {
*c=append(*c, "hello")
}

输出如下:

0 Kathmandu
1 Pokhara
2 Lumbini
3 hello

参考

how-to-append-to-a-slice-pointer-receiver
深度解密Go语言之Slice

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions