day03
一、复合类型(指针)
1. 指针的基本操作
//第个变量有两层含义:变量的内存,变量的地址
var a int = 10
fmt.Printf("%d\n", a) //变量的内存
fmt.Printf("%v\n", &a) //变量的地址
fmt.Printf("%p\n", &a) //变量的地址
fmt.Println(&a) //变量的地址
//保存某个变量的地址,需要指针。 *int 保存int的地址, **int 保存 *int的地址
//定义只是一个特殊的声明
//定义一个变量p,类型为 *int
var p *int
p = &a //指针变量指向谁,就把谁的地址赋值给指针变量
fmt.Println(p)
*p = 666 //*p操作的不是p的内存,是p指向的地址的内存(也就是a)
fmt.Println(*p)
fmt.Println(a)
2. 指针不要操作没有合法指向的内存
go语言指针没有指向时是 nil (即默认值 ,C的默认是null)
var p *int
// p = nil 可以给他赋值成nil
fmt.Println(p)
var p *int
// p = nil 可以给他赋值成nil
fmt.Println(p)
*p = 666
//会报错panic: runtime error: invalid memory address or nil pointer dereference
//[signal 0xc0000005 code=0x1 addr=0x0 pc=0x48b523]
//goroutine 1 [running]:
3. new函数的使用
new相当于C的动态分配空间,但是C需要手动释放内存,而go有自己的自动gc,不需要手动释放
var p *int
p = new(int) //p为指向的是int的内存地址,所以new(int)开辟了一块新的int内存
fmt.Println(p) //会输出p指导指向的内存地址
fmt.Println(*p) //p所指向的内存为int,默认值 是0,所以0
*p = 666
fmt.Println(*p) //p指向的内存新赋值为666,所以输出666
q := new(int) //自动推导
fmt.Println(q)
*q = 777
fmt.Println(*q)
4. 值传递
func swap(a, b int) {
a, b = b, a
fmt.Println("内a ", a)
fmt.Println("内b ", b)
}
func main() {
a, b := 10, 20
swap(a, b) //因为是值传递,不会改变主函数a,b的值
fmt.Println(a)
fmt.Println(b)
}
5. 指针做函数参数(地址传递)
func swap(a, b *int) {
*a, *b = *b, *a
fmt.Println("内a ", a) // 打印出的是内存地址
fmt.Println("内b ", b) // 打印出的是内存地址
}
func main() {
a, b := 10, 20
swap(&a, &b) //因为传递过去的是内存地址(指针),会改变主函数a,b的值
fmt.Println(a)
fmt.Println(b)
}
二、复合类型(数组)
6. 数组声明
数组是同一类型的集合,声明数组时,数组的个数必须是常量
var ids [50]int
//数组的个数为50个 len()
//下标是从0到len()-1
//[数字],是定义数组的个数
// [5]int 和 [10]int 是不同的类型
fmt.Println(len(ids))
//赋值
for i := 0; i < len(ids); i++ {
ids[i] = i + 1
}
for _, data := range ids {
fmt.Println(data)
}
//数组指针
p := &ids
fmt.Println(p)
//&[1 2 3 ...47 48 49 50]
7. 数组的初始化
//全部初始化
var a [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println(a) // [1 2 3 4 5]
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b) // [1 2 3 4 5]
//部分初始化,没初始化的元素,会有默认值
c := [5]int{1, 2, 3}
fmt.Println(c) // [1 2 3 0 0]
d := [5]int{2: 10, 4: 20} //根据下标赋值
fmt.Println(d)
8. 两维数组
有多少个 [ ] 就是多少维数组
有多少个 [ ] 就用多少个循环
var a [3][4]int
var k int
for i := 0; i < 3; i++ {
for j := 0; j < 4; j++ {
k++
a[i][j] = k
fmt.Printf("%d ", a[i][j])
}
fmt.Println("")
}
//1 2 3 4
//5 6 7 8
//9 10 11 12
fmt.Println(a) // [[1 2 3 4] [5 6 7 8] [9 10 11 12]]
//全部初始化
b := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}
fmt.Println(b) // [[1 2 3 4] [5 6 7 8] [9 10 11 12]]
//部分初始化
c := [3][4]int{{1, 2, 3}, {5, 6, 7, 8}, {9, 10}}
fmt.Println(c) // [[1 2 3 0] [5 6 7 8] [9 10 0 0]]
d := [3][4]int{{1, 2, 3}, {5, 6, 7, 8}}
fmt.Println(d) // [[1 2 3 0] [5 6 7 8] [0 0 0 0]]
e := [3][4]int{1: {1, 2, 3}}
fmt.Println(e) // [[0 0 0 0] [1 2 3 0] [0 0 0 0]]
f := [3][4]int{1: {2: 3}}
fmt.Println(f) // [[0 0 0 0] [0 0 3 0] [0 0 0 0]]
9. 数组的比较和赋值
//数组比较
//只支持 == 和 != 而且比较的两个数组类型要一样(比较是不是第一个元素都都一样)
a := [4]int{1, 2, 3, 4}
// b := [3]int{1, 2, 3}
c := [4]int{1, 2, 3, 4}
d := [4]int{1, 2, 3}
// fmt.Println(a == b) 类型不一样,比较会报错
fmt.Println(a == c) //true
fmt.Println(a == d) //false
//数组赋值
e := a
fmt.Println(e) //[1 2 3 4]
10. Go的随机数
//设置种子,只需要一次
// rand.Seed(666)
//如果种了参数一样,每次产生的随机数都是一样的(所以我们有时间)
rand.Seed(time.Now().UnixNano()) //当前系统时间作为种子参数
for i := 0; i < 5; i++ {
//产生随机数
// fmt.Println(rand.Int())
fmt.Println(rand.Intn(100)) //产生100以内的随机数(0-99)
}
11. 数组做函数参数
//数组做函数参数是值传递,实参的每一个元素都都会给形参拷贝一份,
//形参的数据是实参数数据的复制品
func modify(a [4]int) {
a[0] = 666
fmt.Println("modify ", a) //modify [666 2 3 4]
}
func main() {
a := [4]int{1, 2, 3, 4}
modify(a)
fmt.Println("main ", a) //main [1 2 3 4]
}
12. 数组指针传递.go
func modify(a *[4]int) {
//a指向的是数组a的内存地址
(*a)[0] = 666
fmt.Println("modify ", *a) //modify [666 2 3 4]
}
func main() {
a := [4]int{1, 2, 3, 4}
modify(&a) //地址传递
fmt.Println("main ", a) //main [666 2 3 4]
}
三、复合类型(切片slice)
13. 切片说明
数组使用时缺点:大小是固定的,声明时必须指定长度,容量不能扩容,作为函数参数时,会把整个数组都拷贝一遍
切片就是为了弥补数组的缺点,切片不是数组也不是数组的指针(可以理解为可变长的数组)。切片是一种引用类型,底层总是指向一个array,切片的声明也像数组一样,只是不需要长度
14. 切片长度和容量
a := []int{1, 2, 3, 4}
// [low:high:max]
// low: 下标的起点
// high: 下标的终点(不包括些下标)
// leng: 长度 high-low
// cap: 容量 max-low
s := a[1:3:4]
fmt.Println(s) // [2 3]
fmt.Println(len(s)) //切片长度2 3-1
fmt.Println(cap(s)) //切片长度3 4-1
15. 数组和切片区别
//数组和切片的区别
//数组的 [] 里面的长度,是一个常量,数组不能修改长度,len和cap是固定的这里是5
a := [5]int{} //数组
fmt.Println(a) // [0 0 0 0 0]
fmt.Println(len(a)) // 5
fmt.Println(cap(a)) // 5
//切片 [] 里面是空的或者是...( [...]int),切片的长度和容量是不固定的
s := []int{} //切片
fmt.Println(s) // []
fmt.Println(len(s)) // 0
fmt.Println(cap(s)) // 0
s = append(s, 11) //给切片末尾追加一个成员
fmt.Println(s) // [11]
fmt.Println(len(s)) // 1
fmt.Println(cap(s)) // 1
16. 切片的创建
//自动推导,同时初始化
s := []int{1, 2, 3}