1.切片通过函数,传的是什么?
package mainimport ("fmt""reflect""unsafe"
)func main() {s := make([]int, 5, 10)PrintSliceStruct(&s)test(s)
}func test(s []int) {PrintSliceStruct(&s)
}func PrintSliceStruct(s *[]int) {ss := (*reflect.SliceHeader)(unsafe.Pointer(s))fmt.Println("slice struct: %+v, slice is %v\n", ss, s)
}
切片是引用类型:
-
切片在函数间传递时,
SliceHeader
(slice 的内部结构) 会被复制,但Data
指针指向的底层数组是共享的。因此,修改切片元素会影响所有引用该底层数组的切片。
2.在函数里面改变切片,函数外的切片会被影响吗?
package mainimport ("fmt""reflect""unsafe"
)func main() {s := make([]int, 5, 10)case1(s)case2(s)PrintSliceStruct(&s)
}func PrintSliceStruct(s *[]int) {ss := (*reflect.SliceHeader)(unsafe.Pointer(s))fmt.Println("slice struct: %+v, slice is %v\n", ss, s)
}// 底层数组变化
func case2(s []int) {s = append(s, 0)s[1] = 1PrintSliceStruct(&s)
}// 底层数组不变
func case1(s []int) {s[1] = 1PrintSliceStruct(&s)
}
总结:
操作 | 是否影响外部切片 | 说明 |
s[i] = x | ✅ 是 | 直接修改底层数组 |
append(未扩容) | ⚠️ 部分影响(len 不变) | 修改元素会影响,但 len 不会变 |
append(扩容) | ❌ 否 | 新切片指向新数组 |
传递 *[]int | ✅ 是 | 直接修改外部切片 |
3.截取切片
通过操作得到的新slice和原slice是什么关系?
(1) 底层数组共享
-
新切片和原切片指向同一个底层数组,修改其中一个的元素会影响另一个。
(2) 新切片的长度和容量变化
-
新切片的
len
取决于截取的范围(s[low:high]
→len = high - low
)。 -
新切片的
cap
是原切片cap - low
(即从low
开始到原底层数组末尾)。
(3) append
是否触发扩容
-
如果新切片
append
后未超过底层数组容量,修改会影响原切片。 -
如果
append
后超过容量,Go 会分配新数组,此时新切片与原切片不再共享底层数组。
package mainimport ("fmt""reflect""unsafe"
)func main() {s := make([]int, 5, 10)case1(s)case2(s)case3(s)case4(s)PrintSliceStruct(&s)
}func PrintSliceStruct(s *[]int) {ss := (*reflect.SliceHeader)(unsafe.Pointer(s))fmt.Println("slice struct: %+v, slice is %v\n", ss, s)
}// 截取获得新切片
func case4(s []int) {s1 := s[2:]PrintSliceStruct(&s1)
}// 截取[len(s) - 1, ]区间元素
func case3(s []int) {s = s[len(s)-1:]PrintSliceStruct(&s)
}// 截取[1,2]区间元素
func case2(s []int) {s = s[1:3]PrintSliceStruct(&s)
}// 截取0号以后的元素
func case1(s []int) {s = s[1:]PrintSliceStruct(&s)
}
output:
slice struct: %+v, slice is %v&{1374390288392 4 9} &[0 0 0 0]
slice struct: %+v, slice is %v&{1374390288392 2 9} &[0 0]
slice struct: %+v, slice is %v&{1374390288416 1 6} &[0]
slice struct: %+v, slice is %v&{1374390288400 3 8} &[0 0 0]
slice struct: %+v, slice is %v&{1374390288384 5 10} &[0 0 0 0 0]
4.删除元素:
package mainimport ("fmt""reflect""unsafe"
)func main() {s := []int{0, 1, 2, 3, 4}_ = s[4]PrintSliceStruct(&s)s1 := append(s[:1], s[2:]...)PrintSliceStruct(&s1)PrintSliceStruct(&s)// 访问原切片_ = s[4]// 访问原切片中删除了一个元素的切片_ = s1[4]
}func PrintSliceStruct(s *[]int) {ss := (*reflect.SliceHeader)(unsafe.Pointer(s))fmt.Println("slice struct: %+v, slice is %v\n", ss, s)
}
output:
slice struct: %+v, slice is %v&{1374389641552 5 5} &[0 1 2 3 4]
slice struct: %+v, slice is %v&{1374389641552 4 5} &[0 2 3 4]
slice struct: %+v, slice is %v&{1374389641552 5 5} &[0 2 3 4 4]
panic: runtime error: index out of range [4] with length 4goroutine 1 [running]:
main.main()/Users/admin/GolandProjects/Go_study/slice/slice_4/main/slice_4.go:23 +0x1a8
-
关键发现:
s
的内容也被修改了! -
因为
s
和s1
共享底层数组,append操作修改了数组内容
最后一个4是原数组的残留值
5.添加元素
package mainimport ("fmt""reflect""unsafe"
)func main() {case1()case2()case3()}func case3() {s1 := make([]int, 3, 3)s2 := append(s1, 1)PrintSliceStruct(&s1)PrintSliceStruct(&s2)
}func case2() {s1 := make([]int, 3, 4)s2 := append(s1, 1)PrintSliceStruct(&s1)PrintSliceStruct(&s2)
}func case1() {s1 := make([]int, 3, 3)s1 = append(s1, 1)PrintSliceStruct(&s1)
}func PrintSliceStruct(s *[]int) {ss := (*reflect.SliceHeader)(unsafe.Pointer(s))fmt.Println("slice struct: %+v, slice is %v\n", ss, s)
}
Output:
slice struct: %+v, slice is %v&{1374390272048 4 6} &[0 0 0 1]
slice struct: %+v, slice is %v&{1374390296608 3 4} &[0 0 0]
slice struct: %+v, slice is %v&{1374390296608 4 4} &[0 0 0 1]
slice struct: %+v, slice is %v&{1374390231160 3 3} &[0 0 0]
slice struct: %+v, slice is %v&{1374390272096 4 6} &[0 0 0 1]
6.slice深度拷贝的方法