在 Golang 中,数组和切片是存储和处理集合数据的基本结构。为了编写高效、清晰的 Go 程序,必须深入理解它们的特性及内存管理方式。本文将全面讲解数组与切片的声明、初始化、使用、内存布局及常见操作。
1. 数组概述
数组是固定长度、元素类型相同的集合。定义数组时,其长度必须明确指定,且一旦定义就不能改变。
数组声明与初始化
声明数组
1
| var arr [5]int // 长度为5的整型数组,元素默认值为0
|
初始化数组
- 使用大括号进行初始化:
1
| arr := [5]int{1, 2, 3, 4, 5} // 长度为5的数组
|
- 使用
...
自动推断长度:
1
| arr := [...]int{10, 20, 30} // 长度为3
|
- 部分初始化:
1
| arr := [5]int{1: 10, 3: 30} // [0, 10, 0, 30, 0]
|
访问和修改数组元素
通过索引访问和修改元素,索引从0开始:
1 2
| arr[0] = 100 // 修改第一个元素 fmt.Println(arr[0]) // 输出100
|
数组遍历
使用 for
循环遍历数组:
1 2 3
| for i, value := range arr { fmt.Printf("Index: %d, Value: %d\n", i, value) }
|
2. 切片概述
切片是对数组的抽象,提供了更灵活和强大的功能。切片可以动态调整长度,但它仍然依赖底层数组。
切片的声明与初始化
从数组创建切片
1 2
| arr := [5]int{1, 2, 3, 4, 5} slice := arr[1:4] // 包含arr的第2到第4个元素 [2, 3, 4]
|
使用 make
创建切片
1
| slice := make([]int, 3, 5) // 创建长度为3、容量为5的切片
|
使用字面量创建切片
1
| slice := []int{10, 20, 30} // 长度和容量均为3
|
切片的基本操作
追加元素
使用 append
函数向切片添加元素:
1 2
| slice := []int{1, 2, 3} slice = append(slice, 4, 5) // 追加4和5
|
当切片容量不足时,append
会创建新的底层数组并扩容。
切片的切片
可以对切片再进行切片操作:
1
| subSlice := slice[1:3] // 从slice中获取子切片
|
切片拷贝
使用 copy
函数将一个切片复制到另一个切片:
1 2 3
| src := []int{1, 2, 3} dst := make([]int, len(src)) copy(dst, src) // 将src拷贝到dst
|
3. 切片的内存结构
切片包含三部分:指针、长度和容量。
- 指针:指向底层数组中切片起始位置。
- 长度:切片中当前可用的元素数目。
- 容量:从切片起始位置到底层数组末尾的元素数目。
内存共享
切片和底层数组共享内存,因此修改切片中的元素会影响底层数组。
1 2 3 4
| arr := [5]int{1, 2, 3, 4, 5} slice := arr[1:4] slice[0] = 100 // 修改slice,也会影响arr fmt.Println(arr) // 输出 [1, 100, 3, 4, 5]
|
切片扩容机制
当切片容量不足时,append
会分配新的底层数组。扩容策略通常是将容量倍增,以提高效率。
1 2
| slice := []int{1, 2, 3} slice = append(slice, 4, 5, 6) // 容量自动扩增
|
4. 数组与切片的区别
- 长度:数组长度固定,切片长度可变。
- 性能:数组适用于需要确定大小的数据集;切片更灵活,适合处理动态数据集。
- 内存使用:切片使用底层数组的内存,可以节省内存分配开销。
5. 常见操作与注意事项
切片长度与容量
使用 len()
和 cap()
获取切片长度和容量:
1 2 3
| slice := make([]int, 3, 5) fmt.Println("Length:", len(slice)) // 输出3 fmt.Println("Capacity:", cap(slice)) // 输出5
|
零值切片
切片的零值是 nil
,可以用 nil
检查切片是否为空:
1 2 3 4
| var slice []int if slice == nil { fmt.Println("Slice is nil") }
|
深拷贝与浅拷贝
- 浅拷贝:切片复制或切片派生操作会创建新的切片结构,但底层数组共享内存。
- 深拷贝:使用
copy()
函数创建数据独立的拷贝。
6. 示例:数组与切片在实际场景中的应用
使用数组实现固定大小的缓冲区
1 2 3 4 5 6 7
| func main() { var buffer [10]int for i := 0; i < len(buffer); i++ { buffer[i] = i * i } fmt.Println("Buffer:", buffer) }
|
使用切片处理动态数据
1 2 3 4 5
| func main() { data := []string{"Go", "Rust", "Python"} data = append(data, "JavaScript") fmt.Println("Languages:", data) }
|