数组与切片
数组
数组的使用和以前基本差不多,就写一些他们之间的不同点
数组在内存中的布局
- 可以使用&数组名获得数组的内存地址,数组的第一个元素的地址,就是数组的首地址
- 数组之间的地址间隔根据数组元素的类型来决定,比如int32他们就是4个字节的差距
四种初始化数组的方法:
1 | //原始办法 |
结果:
1 | [1 2 3 4] |
切片
切片就是没有固定容量的数组; 切片是一个很小的对象,它对底层的数组(内部是通过数组保存数据的)进行了抽象,并提供相关的操作方法。切片是一个有三个字段的数据结构,这些数据结构包含 Golang 需要操作底层数组的元数据:
1 | a:=[...]int{1,2,3,4,5,6,7,8,9} |

最重要的是切片是一个引用数据类型,就是说我们使用 slice:=a[1:8] 进行的行为是==引用传递==,简单来说,就是我们切片存储的不是值,而是我们的引用数组元素的地址,再由地址指向元素,换句话说,如果数组变了那么切片的数据也会跟着改变
例子
1 | a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9} |
结果:
1 | [1 2 3] |
创建切片的3种方式
第一种,使用已有的数组;
1
2
3a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
//使用数组名 数组名[n:m],就是获得n-m的数据,包括n不包括m
slice := a[:3]使用make来获得一个切片
1
2slice:=make([]int,5,10)
fmt.Println(slice)使用make有3个属性
- type:类型, 使用[]类型表示
- len,切片现在的长度,就是初始化里面的元素数量
- cap:切片的容量,可以动态扩充,如果选择了填写cap,那么cap>=len,切片对应的底层数组的大小为指定的容量
- 如果不对切片赋值,那么所有的都是默认值
直接赋值,直接指明数据
1
2
3
4slice:=[]int{1,2,3}
fmt.Println(slice)
fmt.Println(len(slice)) //3
fmt.Println(cap(slice)) //3如果没有指明容量,那么这个切片的容量和长度一样;我们只可以访问我们在这个长度以内的元素,超出切片长度却又没有超出切片容量的下标,我们是无法访问的,因为 容量代表着底层数组的大小,但是超出大小的部分对我们不可见
一个切片可以以其他切片为主,复制其他的切片
1
2slice:=make([]int,2,10)
slice1:=slice[1:2]//从第一号开始复制,那么他在底层相对于slice就有了一个偏移量1 因为他是从一开始的- 需要记住的是,现在两个切片共享同一个底层数组。如果一个切片修改了该底层数组的共享部分,另一个切片也能感知到
- 子切片的容量为底层数组的长度减去切片在底层数组的开始偏移量
基于第一点,可以使用其他切片作为我们的父切片,且他们之间共享同一个底层数组,意思就是可以被感知到数据的变化,但是当我们对其中的子切片扩容超出自己的容量,那么他就会脱离这个群体,自己成一个数组。这个时候,他对自己数据的改变已经对家庭没有影响了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22slice := make([]int, 2, 10)
fmt.Println(cap(slice))
slice1 := slice[:2]
fmt.Println(slice1)
fmt.Println(cap(slice1))
slice2 := append(slice1, 1)
fmt.Println(cap(slice2))
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2 = append(slice2, 1)
slice2[0] = 10001
fmt.Println(slice)
fmt.Println(slice1)
fmt.Println(cap(slice2))
我们知道原始的 slice2 对应的底层数组的容量为 9,经过我们一系列的 append 操作,原始的底层数组已经无法容纳更多的元素了,此时 Go 会分配另外一块内存,把原始切片从位置 1 开始的内存复制到新的内存地址中,也就是说现在的 slice2 切片对应的底层数组和 slice 切片对应的底层数组完全不是在同一个内存地址,所以当你此时更改 slice2 中的元素时,对 slice 已经来说,反之,slice 操作对slice2没有一点作用,一点儿关系都没有。两个切片的地址就不一样,就是两个完全不一样的切片
二维数组
创建方法和基本使用和一维数组一样
1 | a := [][]int{{1, 2}, {3, 4}} |
在内存中的存储
存储两个地址,分别指向不同地方




