append
函数是Go语言中用于向切片添加元素或将一个切片拼接到另一个切片末尾的函数。它的行为和特性非常灵活和强大,但有时也会引起一些意外的结果。让我们详细说明append
函数的行为和特性。
append
函数的基本用法
append
函数的签名如下:
func append(slice []T, elems ...T) []T
slice
是一个现有的切片。elems
是要追加到切片末尾的元素。
示例
s := []int{1, 2, 3}
s = append(s, 4, 5)
fmt.Println(s) // 输出: [1 2 3 4 5]
append
函数的特性
1. 动态扩展
append
函数会根据需要动态扩展切片的容量。如果原始切片的容量不足以容纳新元素,append
会分配一个新的底层数组,复制原始切片的内容,并将新元素追加到新数组中。
s := make([]int, 0, 3) // 长度为0,容量为3
s = append(s, 1, 2, 3) // 使用已有容量
s = append(s, 4) // 超过容量,分配新数组
fmt.Println(s) // 输出: [1 2 3 4]
2. 返回新切片
append
返回一个新的切片。这个新切片可能指向与原切片相同的底层数组,也可能是一个新的底层数组。
s1 := []int{1, 2, 3}
s2 := append(s1, 4)
fmt.Println(s1) // 输出: [1 2 3]
fmt.Println(s2) // 输出: [1 2 3 4]
3. 共享底层数组
在原切片容量足够的情况下,append
返回的新切片和原切片共享同一个底层数组。这种情况下,对新切片的修改可能会影响原切片。
s1 := []int{1, 2, 3}
s2 := append(s1[:2], 4)
fmt.Println(s1) // 输出: [1 2 4]
fmt.Println(s2) // 输出: [1 2 4]
4. 当容量不够时
如果原切片的容量不够,append
会分配新的底层数组,这样新切片和原切片就不会相互影响。
s1 := []int{1, 2, 3}
s2 := append(s1, 4, 5, 6) // 超过容量,分配新数组
s2[0] = 100
fmt.Println(s1) // 输出: [1 2 3]
fmt.Println(s2) // 输出: [100 2 3 4 5 6]
例子分析
回到你提供的代码:
package main
import "fmt"
func main() {
courseSlice := []string{"Go", "Rust", "C++", "Java", "Python"}
mySlice := append(courseSlice[:2], courseSlice[3:]...)
fmt.Println(mySlice)
courseSlice = courseSlice[:3]
fmt.Println(courseSlice)
}
courseSlice[:2]
取出前两个元素:["Go", "Rust"]
courseSlice[3:]
取出从第四个元素到最后的元素:["Java", "Python"]
append
函数将这两部分拼接成一个新的切片:["Go", "Rust", "Java", "Python"]
由于 append
修改了底层数组,courseSlice
的第三个元素("C++"
)被 append
操作覆盖为 "Java"
。因此,courseSlice
变为:["Go", "Rust", "Java", "Java", "Python"]
然后,courseSlice = courseSlice[:3]
截断为前三个元素:["Go", "Rust", "Java"]
最终的输出是:
[Go Rust Java Python]
[Go Rust Java]
希望这些信息能帮助你更好地理解append
函数的行为和特性。