一、复合类型:
1. 分类:
类型 | 名称 | 长度 | 默认值 | 说明 |
---|---|---|---|---|
pointer | 指针 | nil | ||
array | 数组 | 0 | ||
slice | 切片 | nil | 引用类型 | |
struct | 结构体 |
2. 数组的定义和使用:
数组 是指一系列同一类型的数据集合;
2.1 数组的定义:
var 数组名 [长度]类型
数组定义完之后没有赋值, 默认输出是0;
2.2 数组赋值:
- 使用下标赋值;
- 注意下标越界;
var arr [5] int
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
2.3 匿名变量打印:
func main() {
var arr [10]int
for i := 0; i < 10; i++ {
arr[i] = i + 1
}
for index, ele := range arr {
fmt.Println(index, ele)
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5
// 5 6
// 6 7
// 7 8
// 8 9
// 9 10
}
}
2.4 数组初始化:
全部初始化:
var a [5]int = [5]int{1, 2, 3, 4, 5}
// 自动推导
b := [5]int{1, 2, 3, 4, 5}
部分初始化:
c := [5]int{1,2,3}
// c = [1, 2, 3, 0, 0]
指定索引初始化:
d := [5]int{2: 10, 4: 20}
// d = [0, 0, 10, 0, 20]
自动推导长度:
f := [...]int{1,2,3}
// len(f) = 3
2.5 数据类型:
arr := [5]int{1,2,3,4,5}
fmt.Printf("%T\n", arr) // [5]int
- 数组名表示整个数组;
- 数组名对应的地址就是数组第一个元素对应的地址;
2.6 数组的逆置:
arr[i], arr[j] = arr[j], arr[i]
2.7 冒泡排序:
3. 随机数:
import (
"fmt"
"math/rand"
"time"
)
func main() {
// 1. 导入头文件: math/rand
// 2. 随机数种子;
// 3. 创建随机数
rand.Seed(time.Now().UnixNano())
fmt.Println(rand.Int())
fmt.Println(rand.Intn(10)) // 生成10以内的随机数
}
4. 数组作为函数参数:
func BubbleSort(arr [10]int) [10]int {
for i := 0; i < len(arr)-1; i++ {
for j := 0; j < len(arr)-1-i; j++ {
if arr[j] < arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
return arr
}
func main() {
// 数组作为函数参数是"值传递"
arr := [10]int{9, 5, 3, 1, 4, 5, 8, 6, 10, 8}
arr = BubbleSort(arr)
fmt.Println(arr)
}
5. 二维数组:
var arr [3][4] int // 定义3x4的二维数组
// [[0 0 0 0]
// [0 0 0 0]
// [0 0 0 0]]
// 全部初始化
arr1 := [3][4]int{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}
// [[1 2 3 4] [5 6 7 8] [9 10 11 12]]
// 部分初始化
arr2 := [3][4]int{{1, 2, 3, 4}, {5, 6, 7}}
// [[1 2 3 4] [5 6 7 0] [0 0 0 0]]
6. 切片定义和使用:
6.1 定义
// 定义方式1
var slice []int // 定义空切片 []
fmt.Println(slice)
fmt.Println(len(slice)) // 长度
fmt.Println(cap(slice)) // 容量
// 定义方式2
s1 := []int{1, 2, 3}
fmt.Println(s1)
// 定义方式3: 使用make函数时, 长度值要小于容量值; 容量值可省略
s2 := make([]int, 5, 10) // 定义切片, 长度是5, 容量是10
s2 = append(s2, 1, 2, 3, 4, 5, 6)
fmt.Println(s2) // [0 0 0 0 0 1 2 3 4 5 6]
6.2 使用:
使用参考数组的使用
6.3 切片地址和扩容:
- 在使用append追加数据时, 长度超过容量, 容量会自动扩容;
- 一般新容量是旧容量的2倍, 如果超过1024字节, 每次扩容旧容量的四分之一;
6.4 切片的截取:
- 截取后的切片是原切片的一部分, 修改截取后的切片, 原切片也被修改;
s3 := []int{10, 20, 30, 40, 50, 60}
// 截取 s[low: high: max]
// len = high - low
// cap = max - low
s4 := s3[0:3:5]
fmt.Println(s4) // [10 20 30]
fmt.Println(len(s4)) // 3
fmt.Println(cap(s4)) // 5
6.5 切片的拷贝:
切片拷贝: copy
- 是深拷贝;
- 使用copy进行拷贝, 在内存中存储两个独立的切片内容(内存地址不同)
var s []int = []int{1,2,3,4,5}
s1 := make([]int, 5)
fmt.Println(s1) // [0,0,0,0,0]
copy(s1, s)
fmt.Println(s1) // [1,2,3,4,5]
6.6 切片作为函数参数:
- 切片传参, 传的是地址, 指向同一个地址;
- 使用append修改切片参数, 切片地址可能发生改变;
7.map 字典结构:
7.1 map的定义和使用:
- map不能使用cap, 查看容量, 但是可以指定容量;
// 定义1
var 变量名 map[键类型]值类型
var m2 map[int]string
// 定义2
变量名 := make(map[键类型]值类型, 容量)
m2 := make(map[int]string, 容量)
- 键是唯一的, 值是可以重复的;
- map是无序结构
m2[1] = "张三"
m2[2] = "李四"
m2[3] = "王五"
m2[4] = "张三"
初始化:
m3 := map[int]string{1: "张三", 2: "李四", 3: "王五", 4: "张三"}
func main() {
var m1 map[int]string
fmt.Printf("%p\n", m1) // 0x0
}
- 当内存地址打印为0x0时, 为系统占用, 不允许用户做读写操作;
7.2 map的键值:
- map的键必须支持逻辑运算, 一般建议使用基本类型, 即: 不能使用引用类型变量做key
m2 := make(map[string][3]int)
m2["小明"] = [3]int{97, 98, 99}
m2["小亮"] = [3]int{80, 81, 82}
m2["小红"] = [3]int{100, 101, 102}
fmt.Println(m2) // map[小亮:[80 81 82] 小明:[97 98 99] 小红:[100 101 102]]
for k, v := range m2 {
fmt.Println(k, v)
// 小明 [97 98 99]
// 小亮 [80 81 82]
// 小红 [100 101 102]
}
删除:
delete(map变量, key值)
7.3 map作为函数参数:
- map传参, 传的是地址(或 引用)
8. 结构体:
8.1 结构的定义和使用
- 结构体定义在main函数外
package main
import "fmt"
type 结构体名 struct {
// 结构体成员列表
// 成员名 数据类型
}
// 定义结构体
type Student struct {
id int
name string
sex string
age int
addr string
}
func main() {
// 顺序初始化, 每个成员必须初始化
var s1 Student = Student{1, "小明", "男", 18, "北京市"}
fmt.Println(s1) // {1 小明 男 18 北京市}
// 自动化推导
s2 := Student{name: "小亮", age: 18, sex: "女", id: 2, addr: "朝阳区"}
fmt.Println(s2) // {2 小亮 女 18 朝阳区}
// 定义结构体变量 符合类型
// var 变量名 结构体
var s3 Student
// 赋值
s3.id = 3
s3.name = "小红"
s3.sex = "女"
fmt.Println(s3) // {3 小红 女 0 } 不赋值, 就是类型的默认值
}
// 结构体的地址就是第一个属性的地址
// 结构体所占内存的大小就是所有属性占内存的和
fmt.Printf("%p\n", &s3) // 0xc0000b20c0
fmt.Printf("%p\n", &s3.id) // 0xc0000b20c0
fmt.Printf("%p\n", &s3.name) // 0xc0000b20c8
fmt.Printf("%p\n", &s3.sex) // 0xc0000b20d8
- 结构体可以做恒等比较;
8.2 结构体数组和切片:
- 结构体数组作为函数参数, 是值传递;
- 结构体切片作为函数参数, 是地址(引用)传递;
8.3 结构体和map:
8.4 结构体作为函数参数:
- 结构体作为函数参数, 是值传递;