文章标题 原创 翻译 转载 文章内容 直接用例子说话 例1: ``` func main() { s := make([]int, 1, 3) // 创建一个长度为1,容量为3的切片 fmt.Printf("before: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) modifySlice1(s) fmt.Printf("after: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) } func modifySlice1(s []int) { s = append(s, 2) s[0] = 1 fmt.Printf("modify: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) } ``` 运行结果: ``` before: slice addr 0xc000004480 point 0xc00000a480, val [0], len 1, cap 3 modify: slice addr 0xc000004500 point 0xc00000a480, val [1 2], len 2, cap 3 after: slice addr 0xc000004480 point 0xc00000a480, val [1], len 1, cap 3 ``` * 通过参数传过去的slice地址不一样,说明slice进行了拷贝(见:addr) * 函数里面修改了slice后原先的slice内容也改变了说明他们的底层指向的是同一份数据(见:point) 例2,与上面代码的区别是append了三个元素 ``` func main() { s := make([]int, 1, 3) // 创建一个长度为1,容量为3的切片 fmt.Printf("before: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) modifySlice2(s) fmt.Printf("after: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) } func modifySlice2(s []int) { s = append(s, 2, 3, 4) s[0] = 1 fmt.Printf("modify: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) } ``` 运行结果: ``` before: slice addr 0xc000054420 point 0xc00005c140, val [0], len 1, cap 3 modify: slice addr 0xc0000544a0 point 0xc000088030, val [1 2 3 4], len 4, cap 6 after: slice addr 0xc000054420 point 0xc00005c140, val [0], len 1, cap 3 ``` * 函数里面slice底层point指向的地址变了,是由于append的元素个数超出了原先的cap,底层进行了内存重分配以及数据拷贝 * 函数里面的修改并没有影响到外面的slice 通过上面两个例子就可以看出这样使用slice容易掉入陷阱,函数外面并不知道slice被修改了。 解决方法,遵守这两个规则: * 无返回参数的函数我们不应该去修改slice,只进行读取 * 如果要修改slice那就应该通过返回值返回 所以正确的修改slice的方法是: ``` func main() { s := make([]int, 1, 3) // 创建一个长度为1,容量为3的切片 fmt.Printf("before: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) s = modifySlice3(s) fmt.Printf("after3: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) s = modifySlice4(s) fmt.Printf("after4: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) } func modifySlice3(s []int) []int { s = append(s, 2) s[0] = 1 fmt.Printf("modify3: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) return s } func modifySlice4(s []int) []int { s = append(s, 3, 4, 5, 6) s[0] = 1 fmt.Printf("modify4: slice addr %p point %p, val %v, len %d, cap %d\n", &s, s, s, len(s), cap(s)) return s } ``` 输出结果: ``` before: slice addr 0xc00004e420 point 0xc000054140, val [0], len 1, cap 3 modify3: slice addr 0xc00004e4a0 point 0xc000054140, val [1 2], len 2, cap 3 after3: slice addr 0xc00004e420 point 0xc000054140, val [1 2], len 2, cap 3 modify4: slice addr 0xc00004e540 point 0xc000080030, val [1 2 3 4 5 6], len 6, cap 6 after4: slice addr 0xc00004e420 point 0xc000080030, val [1 2 3 4 5 6], len 6, cap 6 ``` 这样不管底层数据有没有产生拷贝,表现在外的值都是一致的。 结论: * 函数返回值没有返回slice时,应该只读 * 函数修改了slice时,应该通过返回值返回slice * 如果在函数里面确实要修改slice而又不想改变外面的值时,您应该深拷贝一份slice再进行修改。 文章类别 Python Mobile Android Java Shell Life Database Bug Windows IOS Tools Boost Node.js Mac Product Tips C/C++ Golang Javascript React Qt MQ MongoDB Design Web Linux LLM ChatGPT RAG AI 提交