栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

一起剖析Go语言切片

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一起剖析Go语言切片

Go切片是什么?

我们知道,Go语言数组的定长性和值拷贝限制了其使用场景,Go提供另一种数据类型slice,可以看着一种变长数组,其数据结构中有指向数组的指针,所以是一种引用类型。在开发中关于切片和数组怎么选择?

先看看数组有什么问题?

Go 数组是值类型,赋值和函数传参操作都会复制整个数组数据,下面代码是验证Go都是值复制:

func main() {
	aa := [3]int{9,9,6}
	var bb [3]int
	bb = aa
	fmt.Printf("aa=%p,%dnbb=%p,%d",&aa,aa,&bb,bb)
}

输出结果:

aa=0xc0000b4000,[9 9 6]
bb=0xc0000b4018,[9 9 6]

两个地址都不一样,这样可以证明它们赋值和函数传参都是值复制。

那么问题就暴露出来,假想每次传参都用数组,每次数组都要被复制一遍。数组大小有 100万,在64位机器上就需要花费大约 800W 字节,即 8MB 内存。这样会消耗掉大量的内存。那么,就要解决这个问题?Go语言开发者,想到了使用函数传参用数组的指针。

func main() {
	aa := []int{9,9,6}
	test(&aa) //传数组指针
	bb := aa[:] //切片
	test(&bb)
	fmt.Printf("aa=%p %vn",&aa,&bb)
}
func test(num *[]int) {
	fmt.Printf("func Array : %p , %vn", num, *num)
	(*num)[1] += 100
}

输出结果:

func Array : 0xc00011c018 , [9 9 6]
func Array : 0xc00011c048 , [9 109 6]
aa=0xc00011c018 &[9 209 6]

指针确实到达了我们想要的效果了,但是指针有个问题,打印结果可以看到,第一行和第三行指针地址都是同一个,万一原数组的指针指向更改了,那么函数里面的指针指向都会跟着更改。

切片的优势也就表现出来了。用切片传数组参数,既可以达到节约内存的目的,也可以达到合理处理好共享内存的问题。打印结果第二行就是切片,切片的指针和原来数组的指针是不同的。

由此我们可以得出结论:把第一个大数组传递给函数会消耗很多内存,采用切片的方式传参可以避免上述问题。切片是引用传递,所以它们不需要使用额外的内存并且比使用数组更有效率。

但是,并非所有时候都适合用切片代替数组,因为切片底层数组可能会在堆上分配内存,而且小数组在栈上拷贝的消耗也未必比make 消耗大。

如何创建切片?

(1)由数组创建

语法结构:arr[i:n],其中,arr是数组,i表示开始索引,默认为0,n表示结束索引,默认len(arr)。

Arr[i:n]表示创建一个包含i-n个元素的切片,第一个元素是arr[i],最后一个元素是arr[n-1],如

var arr = [...]int{1,2,3,4,5,6}
s := arr[0:4]

(2)通过内置函数 make 创建切片

注:由 make 创建的切片各元素被默认初始化为切片元素类型 的零值 。 例如:

a := make([]int,10,15)

创建一个长度为,len=10,cap=15

(3)切片基本操作

  • Len()
  • Cap()
  • Append()
  • Copy()
	a := make([]int,1,3) //创建一个切片为int类型,len=1,cap=3
	a = append(a,1)
	a = append(a,1) //已经满啦
	fmt.Printf("len = %d,cap = %d,slice = %vn", len(a), cap(a),a)
	a = append(a,1) //cap会自动扩容,以原来的2倍扩容
	fmt.Printf("len = %d,cap = %d,slice = %vn", len(a), cap(a),a)

输出:

len = 3,cap = 3,slice = [0 1 1]
len = 4,cap = 6,slice = [0 1 1 1]
Slice原理

切片的结构体由3部分构成,Pointer 是指向一个数组的指针,len 代表当前切片的长度,cap 是当前切片的容量。cap 总是大于等于 len 的。

  • ptr 指向Slice开头的元素
  • len 是指说明Slice长度多少,方括号取值只能去到len里面的值
  • cap 是代表整个arr从ptr开始到结束长度

注:Slice是可以向后扩展,但不能向前扩展。取值不可以超过底层数组cap长度

Slice如何扩容

首先,当添加元素超过capacity的长度的时,系统会重新分配更大的底层数组,旧的数组如有还有用就还在,不用Go语言垃圾回收机制自动清除,Slice触发扩容,是以原来数组*2(2倍)进行扩容。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/664948.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号