一、快速记忆先记核心方面数组切片本质固定长度的值指向底层数组的视图传参时整个复制只复制描述信息24字节函数内改元素不影响外面影响外面函数内改长度不允许长度固定外面看不到需返回二、详细对比4个方面1️⃣ 内存和性能数组传参复制全部数据func consumeArray(arr [1000000]int) { // 100万个int约8MB // 函数调用时会完整复制这8MB数据 } func main() { huge : [1000000]int{} consumeArray(huge) // 慢复制8MB }切片传参只复制小纸条func consumeSlice(slice []int) { // 只复制24字节指针长度容量 // 不管底层数组多大都只复制24字节 } func main() { huge : make([]int, 1000000) // 底层8MB数组 consumeSlice(huge) // 快只复制24字节 }结论大数组用切片传参性能好得多。2️⃣ 修改内部元素的影响package main import fmt // 数组改副本不影响外面 func modifyArray(arr [3]int) { arr[0] 999 } // 切片改原数组影响外面 func modifySlice(slice []int) { slice[0] 999 } func main() { // 数组测试 arr : [3]int{1, 2, 3} modifyArray(arr) fmt.Println(数组传参后:, arr) // [1 2 3] 没变 // 切片测试 slice : []int{1, 2, 3} modifySlice(slice) fmt.Println(切片传参后:, slice) // [999 2 3] 变了 }输出数组传参后: [1 2 3] 切片传参后: [999 2 3]3️⃣ 修改长度增删元素这是最坑的地方细看package main import fmt // 尝试在函数内增加元素 func addElement(s []int) { s append(s, 100) // 增加一个元素 fmt.Println(函数内部:, s) // [1 2 3 100] } func main() { slice : []int{1, 2, 3} addElement(slice) fmt.Println(函数外部:, slice) // [1 2 3] 没变 }为什么因为切片有3个信息ptr指针指向底层数组len长度当前有多少元素cap容量最多能装多少传参时复制了这3个信息所以函数内的len是3append后变成4但改的是副本外面的len仍然是3解决方案返回新切片func addElement(s []int) []int { s append(s, 100) return s // 返回新的切片 } func main() { slice : []int{1, 2, 3} slice addElement(slice) // 接收返回值 fmt.Println(slice) // [1 2 3 100] 成功 }4️⃣ 函数签名类型系统// 数组长度是类型的一部分 func process(arr1 [3]int) {} // 只能接收长度3的数组 func process(arr2 [5]int) {} // 这是不同的类型 // 切片长度不是类型的一部分 func process(slice []int) {} // 可以接收任何长度的int切片 func main() { arr3 : [3]int{1, 2, 3} arr5 : [5]int{1, 2, 3, 4, 5} process(arr3) // ✅ 可以 process(arr5) // ❌ 编译错误类型不匹配 slice : []int{1, 2, 3} process(slice) // ✅ 可以 slice2 : []int{1, 2, 3, 4, 5} process(slice2) // ✅ 也可以 }结论切片更灵活数组太死板。三、特殊情况切片扩容当append时容量不够会重新分配底层数组func modifyWithAppend(s []int) { s append(s, 4) // 如果容量够继续用原数组 s[0] 100 } func main() { // 情况1容量够 slice1 : make([]int, 3, 10) // 长度3容量10 slice1[0], slice1[1], slice1[2] 1, 2, 3 modifyWithAppend(slice1) fmt.Println(容量够:, slice1) // [100 2 3] 元素变了但长度还是3 // 情况2容量不够 slice2 : []int{1, 2, 3} // 长度3容量3不够了 modifyWithAppend(slice2) fmt.Println(容量不够:, slice2) // [1 2 3] 完全没变 // 因为append时重新分配了新数组跟外面的数组没关系了 }四、如何选择实际开发建议你的需求选择理由日常开发数据会变动切片99%的情况都用切片数组大小固定不变如坐标点数组或切片均可但切片更灵活函数需要修改长度切片 返回新切片这是标准做法想保护数据不被函数修改数组或复制切片用copy()复制切片性能极端敏感要避免扩容切片 make预分配容量make([]int, 0, 1000)函数间传递超大数据切片必须是切片避免复制开销五、实用技巧技巧1想保护切片不被修改func protectData(original []int) { // 复制一份 copy : make([]int, len(original)) copyData : append([]int{}, original...) // 更简洁的复制 // 让其他函数用副本 dangerousFunction(copyData) }技巧2明确需要修改切片func modifySlice(s []int) []int { // 修改... s append(s, 999) return s // 始终返回 } // 调用时 mySlice modifySlice(mySlice)技巧3面试常问的坑func test(s []int) { s append(s, 4) s[0] 100 } func main() { s : []int{1, 2, 3} test(s) fmt.Println(s) // 请问输出什么 } // 答案[1 2 3] // 因为append后s变了但外面的s没变总结口诀数组笨重传全部改了外面不关注。切片轻巧传地址增删改要懂规矩。改元素时内外通要变长度得返回送。最后记住实际写代码99%都用切片数组只有在极特殊场景比如需要固定长度类型的key、极小数组才用。