yumaojun03 54415d45ba ```
feat(day02): 添加Go语言基础数据类型教程

新增map和slice详细教程,完善array和struct内容

- 新增map章节,详细介绍Go中map的概念、创建、操作和底层原理
- 新增slice章节,涵盖切片的创建、操作、引用特性和实际应用
- 完善array章节,添加数组作为函数参数、多维数组等内容
- 更新struct章节,修正标题错误并补充结构体定义说明
- 为各章节添加实践作业题目,增强学习效果
```
2026-01-11 13:56:26 +08:00
..
```
2026-01-11 13:56:26 +08:00
```
2026-01-11 13:56:26 +08:00
```
2026-01-11 13:56:26 +08:00

切片

alt text

Go中的slice依赖于数组它的底层就是数组所以数组具有的优点, slice都有。 且slice支持可以通过append向slice中追加元素长度不够时会动态扩展通过再次slice切片可以得到得到更小的slice结构可以迭代、遍历等

// runtime/slice.go type slice struct { array unsafe.Pointer // 数组指针 len int // 长度 cap int // 容量 } 每一个slice结构都由3部分组成

  • 容量(capacity): 即底层数组的长度表示这个slice目前最多能扩展到这么长
  • 长度(length)表示slice当前的长度即当前容纳的元素个数
  • 数组指针(array): 指向底层数组的指针

比如创建一个长度为3容量为5int类型的切片

s := make([]int, 3, 4)
fmt.Println(a, len(s), cap(s)) // [0 0 0] 3 5

alt text

创建和初始化

原理:切片是动态数组,底层基于固定大小的数组,可以根据需要动态扩容。创建时可以指定长度和容量。

// 使用 make 创建切片make([]类型, 长度, 容量)
s1 := make([]int, 3, 5)  // 长度3容量5
fmt.Println(s1, len(s1), cap(s1))  // [0 0 0] 3 5

// 直接初始化
s2 := []int{1, 2, 3}  // 长度和容量都是3
fmt.Println(s2, len(s2), cap(s2))  // [1 2 3] 3 3

// 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
s3 := arr[1:4]  // 从索引1到3不包括4
fmt.Println(s3, len(s3), cap(s3))  // [2 3 4] 3 4

切片访问

原理切片通过索引访问元素索引从0开始。与数组类似但更灵活。

s := []int{10, 20, 30, 40, 50}

// 通过索引访问元素
fmt.Println(s[0])  // 10
fmt.Println(s[2])  // 30

// 修改元素
s[1] = 25
fmt.Println(s)  // [10 25 30 40 50]

nil和空切片

原理nil切片表示未初始化的切片长度和容量都为0。空切片是已初始化的切片但不包含元素。两者在行为上略有不同。

// nil 切片:未初始化的切片
var s1 []int
fmt.Println(s1 == nil)  // true
fmt.Println(len(s1), cap(s1))  // 0 0

// 空切片:已初始化的空切片
s2 := []int{}
s3 := make([]int, 0)
fmt.Println(len(s2), cap(s2))  // 0 0
fmt.Println(len(s3), cap(s3))  // 0 0

往切片中添加元素

原理使用append函数添加元素。如果容量不足会自动扩容底层数组通常容量翻倍增长。

s := []int{1, 2}
fmt.Println(s, len(s), cap(s))  // [1 2] 2 2

// 使用 append 添加元素
s = append(s, 3)
fmt.Println(s, len(s), cap(s))  // [1 2 3] 3 4  (容量自动扩容)

// 添加多个元素
s = append(s, 4, 5)
fmt.Println(s)  // [1 2 3 4 5]

通过切片创建新的切片

原理切片操作创建新的切片视图共享底层数组。新切片的长度是high-low容量是原容量减去low。

s := []int{0, 1, 2, 3, 4, 5}

// 创建子切片s[low:high] (不包括high)
sub1 := s[1:4]  // [1, 2, 3]
fmt.Println(sub1)

// 省略索引
sub2 := s[:3]   // [0, 1, 2]
sub3 := s[2:]   // [2, 3, 4, 5]
sub4 := s[:]    // [0, 1, 2, 3, 4, 5] 复制整个切片

遍历切片

原理可以使用传统for循环或range关键字遍历。range返回索引和值更简洁。

s := []string{"apple", "banana", "cherry"}

// 方法1使用索引
for i := 0; i < len(s); i++ {
    fmt.Println(i, s[i])
}

// 方法2使用 range推荐
for index, value := range s {
    fmt.Println(index, value)
}

slice是引用类型

原理:切片是引用类型,赋值时复制切片头,但共享底层数组。修改一个会影响另一个。

s1 := []int{1, 2, 3}
s2 := s1  // s2 指向同一个底层数组

s2[0] = 99
fmt.Println(s1)  // [99 2 3]  s1 也被修改了
fmt.Println(s2)  // [99 2 3]

切片拷贝

原理使用copy函数进行深拷贝创建独立的数据副本。修改拷贝后的切片不会影响原切片。

s1 := []int{1, 2, 3}
s2 := make([]int, len(s1))
copy(s2, s1)  // 拷贝元素到 s2

s2[0] = 99
fmt.Println(s1)  // [1 2 3]  s1 不变
fmt.Println(s2)  // [99 2 3]

切片作为函数参数

原理:切片作为参数传递时,是引用传递,函数内修改会影响调用者。高效但需注意副作用。

func sum(nums []int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

func main() {
    s := []int{1, 2, 3, 4, 5}
    result := sum(s)
    fmt.Println(result)  // 15
}

总结

切片是 Golang 中比较有特色的一种数据类型,既为我们操作集合类型的数据提供了便利的方式,是又能够高效的在函数间进行传递,因此在代码中切片类型被使用的相当广泛

作业

请同学们完成以下切片练习题以巩固对Go语言切片的理解

  1. 创建和初始化切片
    创建一个字符串切片,包含"Go", "Python", "Java",并打印长度和容量。

  2. 切片操作
    给定切片[]int{1,2,3,4,5}创建子切片包含第2到第4个元素不包括第4个并打印结果。

  3. 添加元素
    从空切片开始使用append添加5个整数观察长度和容量的变化。

  4. 遍历切片
    遍历一个整数切片,计算所有元素的和。

  5. 切片拷贝
    创建一个切片,拷贝到另一个切片,修改原切片,验证拷贝是否独立。

  6. 函数参数
    编写一个函数,接受字符串切片作为参数,将所有字符串转换为大写并返回新切片。

请将代码写在 main.go 文件中,并运行测试。