golang数组切片详解

来源:互联网 发布:java技术培训 编辑:程序博客网 时间:2024/05/29 18:03

一.数组切片的使用:

func main() {//1.基于数组创建数组切片var array [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}var slice = array[1:7] //array[startIndex:endIndex] 不包含endIndex//2.直接创建数组切片slice2 := make([]int, 5, 10)//3.直接创建并初始化数组切片slice3 := []int{1, 2, 3, 4, 5, 6}//4.基于数组切片创建数组切片slice5 := slice3[:4]//5.遍历数组切片for i, v := range slice3 {fmt.Println(i, v)}//6.len()和cap()var len = len(slice2) //数组切片的长度var cap = cap(slice)  //数组切片的容量fmt.Println("len(slice2) =", len)fmt.Println("cap(slice) =", cap)//7.append() 会生成新的数组切片slice4 := append(slice2, 6, 7, 8)slice4 = append(slice4, slice3...)fmt.Println(slice4)//8.copy() 如果进行操作的两个数组切片元素个数不一致,将会按照个数较小的数组切片进行复制copy(slice2, slice3) //将slice3的前五个元素复制给slice2fmt.Println(slice2, slice3)}
二.数组切片数据结构分析:

数组切片slice的数据结构如下,一个指向真实array地址的指针ptr,slice的长度len和容量cap




// slice 数据结构type slice struct {array unsafe.Pointer len   int            cap   int            }
当传参时,函数接收到的参数是数组切片的一个复制,虽然两个是不同的变量,但是它们都有一个指向同一个地址空间的array指针,当修改一个数组切片时,另外一个也会改变,所以数组切片看起来是引用传递,其实是值传递。

三.append()方法解析:

3.1 数组切片不扩容的情况

运行以下代码思考一个问题:s1和s2是指向同一个底层数组吗?

func main() {array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9}s1 := array[:5]s2 := append(s1, 10)fmt.Println("s1 =", s1)fmt.Println("s2 =", s2)s2[0] = 0fmt.Println("s1 =", s1)fmt.Println("s2 =", s2)}
输出结果:

s1 = [1 2 3 4 5]
s2 = [1 2 3 4 5 10]
s1 = [0 2 3 4 5]
s2 = [0 2 3 4 5 10]
由第一行和第二行结果看来,似乎这是指向两个不同的数组;但是当修改了s2,发现s1也跟着改变了,这又表明二者是指向同一个数组。到底真相是怎样的呢?

运行以下代码:

import ("fmt""unsafe")type Slice struct {ptr unsafe.Pointer // Array pointerlen int            // slice lengthcap int            // slice capacity}func main() {array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9}s1 := array[:5]s2 := append(s1, 10)s2[0] = 0// 把slice转换成自定义的 Slice structslice1 := (*Slice)(unsafe.Pointer(&s1))fmt.Printf("ptr:%v len:%v cap:%v \n", slice1.ptr, slice1.len, slice1.cap)slice2 := (*Slice)(unsafe.Pointer(&s2))fmt.Printf("ptr:%v len:%v cap:%v \n", slice2.ptr, slice2.len, slice2.cap)}
输出结果:

ptr:0xc04205e0a0 len:5 cap:20 
ptr:0xc04205e0a0 len:6 cap:20

由结果可知:ptr指针存储的是数组中的首地址的值,并且这两个值相同,所以s1和s2确实是指向同一个底层数组。但是,这两个数组切片的元素不同,这个可以根据首地址和数组切片长度len来确定不同的数组切片应该包含哪些元素,因为s1和s2虽然指向同一个底层数组,但是二者的len不同。通过这个demo,也验证了数组切片传参方式也是值传递。
3.2 数组切片扩容的情况:

运行以下代码,思考与不扩容情况的不同之处,以及为什么

func main() {s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}s2 := append(s1, 10)fmt.Println("s1 =", s1)fmt.Println("s2 =", s2)s2[0] = 0fmt.Println("s1 =", s1)fmt.Println("s2 =", s2)}

输出结果:

s1 = [1 2 3 4 5 6 7 8 9]
s2 = [1 2 3 4 5 6 7 8 9 10]
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [0 2 3 4 5 6 7 8 9 10] 

根据结果我们发现,修改s2后,s1并未改变,这说明当append()后,s1和s2并未指向同一个底层数组,这又是为什么呢?

同样,我们接着运行以下代码:

import ("fmt""unsafe")type Slice struct {ptr unsafe.Pointer // Array pointerlen int            // slice lengthcap int            // slice capacity}func main() {s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}s2 := append(s1, 10)fmt.Println("s1 =", s1)fmt.Println("s2 =", s2)s2[0] = 0fmt.Println("s1 =", s1)fmt.Println("s2 =", s2)// 把slice转换成自定义的 Slice structslice1 := (*Slice)(unsafe.Pointer(&s1))fmt.Printf("ptr:%v len:%v cap:%v \n", slice1.ptr, slice1.len, slice1.cap)slice2 := (*Slice)(unsafe.Pointer(&s2))fmt.Printf("ptr:%v len:%v cap:%v \n", slice2.ptr, slice2.len, slice2.cap)}
输出结果:

s1 = [1 2 3 4 5 6 7 8 9]
s2 = [1 2 3 4 5 6 7 8 9 10]
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [0 2 3 4 5 6 7 8 9 10]
ptr:0xc04207a000 len:9 cap:9 
ptr:0xc04207c000 len:10 cap:18 
由结果可知:append()后,s1和s2确实指向了不同的底层数组,并且二者的数组容量cap也不相同了。过程是这样的:当append()时,发现数组容量不够用,于是开辟了新的数组空间,cap变为原来的两倍,s2指向了这个新的数组,所以当修改s2时,s1不受影响


有参考这篇文章:http://www.cnblogs.com/hutusheng/p/5492418.html?hmsr=studygolang.com&utm_medium=studygolang.com&utm_source=studygolang.com


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 拐棍舞 金拐棍计算器 拐棍棒棒糖 拐棍糖 拐棍鱼 残疾人拐棍 拄拐棍 拐棍头 拐棍糖机器 拐点 拐点定义 刘易斯拐点 拐点的定义 拐点怎么求 函数拐点 驻点和拐点 历史的拐点 拐点坐标 路易斯拐点 如何求拐点 拐点和驻点的区别 中国人口拐点 成交量拐点选股 拐点是点还是坐标 函数拐点的定义 成交量拐点选股公式 科二s弯的四个拐点图解 拐角 拐角码头 拐角的英文 拐角录像厅 拐角柜 拐角橱柜 衣柜拐角 拐角书柜 拐角图片 拐角英文 楼梯拐角读后感 拐角书桌书柜组合 卧室拐角飘窗装修效果图 拐角衣柜图片