在 Go 语言中,当传递 []Item
类型的参数给函数时,会拷贝 slice
的头部信息,但不会拷贝底层数组的数据。
具体来说:
Slice (切片) 的头部信息: Go 的
slice
类型实际上是一个结构体,包含三个字段:pointer
: 指向底层数组的指针。length
: 切片的长度。capacity
: 切片的容量。
传递过程: 当将
[]Item
类型的变量传递给函数时,会拷贝这个包含pointer
,length
, 和capacity
的结构体。 也就是说,函数接收到的是一个新的slice
结构体,但这个新的slice
结构体中的pointer
仍然指向同一个底层数组。
这意味着:
函数内部对
slice
的修改可能影响外部:修改
slice
中的元素: 由于slice
指向的是同一个底层数组,如果函数修改了slice
中的Item
元素,那么原始slice
也会受到影响,因为它们共享底层数组。修改
slice
的长度: 如果函数通过append
操作改变了slice
的长度,可能会导致函数内部的slice
使用一个新的底层数组。在这种情况下,原始slice
不会受到影响。 只有当append
操作在函数内部没有超出原始slice
的capacity
时,对长度的修改才会影响到原始slice
。
函数内部对
slice
头部信息的修改不影响外部:- 修改
slice
的长度或容量(但未超出 capacity): 如果函数修改了传入slice
的长度,但未超出原始slice
的capacity
,则原始slice
会受到长度变化的影响。 - 修改
slice
的长度或容量 (超出 capacity): 如果函数修改了传入slice
的长度,且超过了原始slice
的capacity
,Go 会分配一个新的底层数组,并且将数据复制到新的数组中。 在这种情况下,函数内部的slice
就不再和原始slice
共享同一个底层数组,因此原始slice
不会受到影响。
- 修改
总结:
当传递 []Item
时,传递的是 slice
结构体的副本,指向同一个底层数组。 因此:
- 修改
slice
元素的会影响原始slice
。 - 如果
append
操作没有超出原始slice
的capacity
,修改slice
的长度会影响原始slice
。 - 如果
append
操作超出了原始slice
的capacity
,修改slice
的长度不会影响原始slice
。
示例代码:
1 | package main |
输出结果 (可能会因为 Go 版本而略有不同,但核心概念一致):
1 | Original slice (before): [{Item 1 5} {Item 2 10}] len: 2 cap: 2 |
解释:
items[0].Name = "Updated Item"
修改了原始slice
的第一个元素。- 第一次
append
因为没有超过原始 capacity(2), 这时候,长度改变会影响到原始 slice. - 第二次
append
因为超过了原始 capacity(2),会分配一个新的底层数组,所以原始slice
最终没有受到第二次append带来的影响
因此,在 Go 中传递 slice
时要特别注意,理解它是按值传递(但底层数组是指针)的性质,避免产生意料之外的副作用。 如果你希望完全避免修改原始 slice
,可以在函数内部创建一个 slice
的副本,然后操作副本。 例如: copiedSlice := make([]Item, len(items)); copy(copiedSlice, items)
。 然后函数操作copiedSlice
。
Related Issues not found
Please contact @yiGmMk to initialize the comment