Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slice 的问题 #124

Open
codcodog opened this issue Oct 28, 2020 · 0 comments
Open

slice 的问题 #124

codcodog opened this issue Oct 28, 2020 · 0 comments
Labels

Comments

@codcodog
Copy link
Owner

slice 的问题

场景

package main

import "fmt"

type cities []string

func main() {
	Nepal := cities{"Kathmandu", "Pokhara", "Lumbini"}
	Nepal.add()
	Nepal.print()
}

func (c cities) print() {
	for i, city := range c {
		fmt.Println(i, city)
	}
}

func (c cities) 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"

type cities []string

func main() {
	Nepal := cities{"Kathmandu", "Pokhara", "Lumbini"}
	Nepal.add()
	Nepal.print()
}

func (c cities) print() {
	fmt.Printf("print(): %p \n", c)
	for i, city := range c {
		fmt.Println(i, city)
	}
}

func (c cities) 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant