go 切片的传递

文章目录

概要

讲一下go中的切片,以及切片的传递

代码示例

对切片的一个理解

	arr := [5]int{}
	fmt.Println(len(arr)) //5
	fmt.Println(cap(arr)) //5
	as := arr[1:3]        //含头不含尾 包含1不包含3即1,2
	fmt.Println(len(as))  //2 长度指的是切片这个窗口的大小即[1:3]=>1,2
	fmt.Println(cap(as))  //4 容量指的是切片可以操作的最大空间,定义的窗口是1:3,
						  //  窗口可以不断扩大,在底层数组不变的基础上,最大的窗口是1:len(arr)
					      //  你就理解你买了个石头,从中间位置给石头擦了个窗,前面的你不能擦了,后面的你可以继续擦,同一块石头
					      //  擦到石头末尾就不能再擦了,再擦就擦到手了
	fmt.Println(&as[0])   //0x140005e2f38 切片这个窗口的第一个元素的地址,切片的第一个元素对应数组就是arr[1]
	fmt.Println(&arr[1])  //0x140005e2f38
	//append下面再讲,此处先忽略
	as = append(as, 666, 666, 666)
	fmt.Println(len(as)) //5 之前切片映射的是1跟2下标,在这个基础上添加了三个666的元素,所以长度为5 此时切片的值为 0,0,666,666,666
	fmt.Println(cap(as)) //8 发现扩容了,扩容的规则百度一下吧,一个1024内2倍  1024外1.25倍 大概是这样
	fmt.Println(&as[0])  //0x1400061abc0 印证了确是发生了扩容,之前的地址是0x140005e2f38,上面注释有打印说明哈

	ints := make([]int, 0, 10) //make出来的切片的使用规则完全跟上面手动指定的方式是一样的
	fmt.Println(len(ints))
	fmt.Println(cap(ints))
	fmt.Println(&ints[0]) //唯一不同的是这个地方,有些博客说make底层会搞一个默认的数组,既然有默认的数组为什么这行代码会panic呢
						  //我认为在make的时候必须指定长度>0,那么底层才会创建一个数组

在使用中,对append的理解

	arr := [5]int{}
	fmt.Println(arr)    //0.0.0.0.0
	as := arr[1:3]
	fmt.Println(as)     //0.0  对应的是数组arr[1]跟arr[2]
	_ = append(as, 123) //有意思的是这个,为什么一定要 as = append 呢? 为什么 _ = append 切片看起来没变,数组却变了呢
	_ = append(as, 234) //而且为什么123没有插入成功呢
	fmt.Println(arr)    //0.0.0.234.0
	fmt.Println(as)     //0.0
	//我理解append的作用就是更新,或者说刷新,而且是作用于当前切片(slice struct)的更新或者是刷新,因为切片底层的struct中维护了指向数组的指针,
	//切片的长度跟容量,在案例中,我们的切片长度是2容量是4,那么在这个基础上的append 123,相当于go会根据你当前的长度2并根据指向数组的指针ptr,
	//进行append 123那不就相当于做了一个这样的操作吗:arr[1+2] = 123(切片是从数组的1号下标开始的,切片的长度为2),那么做完了这样的一个操作,
	//是不是需要维护一下我们slice struct中的len字段呢?当然需要!所以你必须把append的结果赋值给as,至于append会创建一个slice struct
	//还是刷新旧的slice struct我们不用在意,只需要知道,你必须让append的结果刷回去(我个人更倾向于是创建了一个新的struct 因为这本身并没有什么开销,
	//一个struct中就ptr len cap三个字段,只是为了好理解,更好的形容append做了什么,理解为刷新也是可以的)
	
    //那么回到我们的例子,为什么123没有插入?其实是插入了,只是被234覆盖了罢了
    //为什么123 234都插入了同一个位置?那是因为我们说过,append是对当前的切片做的一个操作,当前的切片进行append可不就是把数组的3号下标的元素修改了吗,
    //而且第一次123的修改,你没有及时的刷新切片,导致切片的len字段还是2,那么234的插入可不就还是插入到了3号下标位

如何不刷新的基础上append多个元素?

	arr := [5]int{}
	fmt.Println(arr)         //0.0.0.0.0
	as := arr[1:3]
	fmt.Println(as)          //0.0
	_ = append(as, 123, 234) //在不刷新的情况下,想要批量插入多个值,可以跟上多个要插入的值
	fmt.Println(arr)         //0.0.0.123.234 数组的值改了,因为append就是去改你的数组的值的,改完了数组的值之后才是slice刷新
	fmt.Println(as)          //0.0 同样不刷新的话还是感知不到,因为slice struct的len跟cap没有变,
	                         // 那么可以理解窗口的大小没有变,那么值怎么可能会变呢(数组的值变了,只是你看不到,因为窗口就那么大,你又没有透视眼,对吧)

我把上面这个例子变化一下,大家再猜猜是多少

	arr := [5]int{}
	fmt.Println(arr) //0.0.0.0.0
	as := arr[1:3]
	fmt.Println(as) //0.0
	_ = append(as, 123, 234, 345)
	fmt.Println(arr) //0.0.0.0.0  为什么会是0呢,其实很简单,数组的长度跟容量就是5,那么你操作arr[5]会报错对吧,下标越界
					//同样切片这个窗口对应的位置是数组的1号跟2号下标,在这个基础上你添加了三个元素,即数组的1号位0 2号位0 3号位123 4号位234 5号位345
					//但是5号位就已经下标越界了啊,所以呢,append其实会给你另创建一个底层数组,并把原始数组的内容拷贝过去
	fmt.Println(as) //0.0

那么创建出来的新的数组怎么看呢?我怎么知道它确实是创建了呢,验证一下

	arr := [5]int{}
	fmt.Println(arr) 			   //0.0.0.0.0
	as := arr[1:3]
	fmt.Println(as)                //0.0
	fmt.Println(&as[0])            //0x140003c6368
	as = append(as, 123, 234, 345) //刷一下,或者你重新赋值给其他的变量引用都是OK的
	fmt.Println(&as[0])            //0x140001e0140     确实是变了上面的数组地址是:0x140003c6368
	fmt.Println(arr)               //0.0.0.0.0  因为重新搞了一个数组,所以上面的append操作应用在了新的数组上arr没有变
	fmt.Println(as)                //[0 0 123 234 345] 底层的数组已经变了

再来聊一下切片在go的函数中的传递

func main() {
	arr := [5]int{}
	as := arr[1:3]
	M1(as)
	fmt.Println(as) //0.0
	//go中的参数传递全是值拷贝
	//main中的as变量在传递到M1方法中,就是做了一次拷贝
	//你可以理解,as跟c是两个struct,只是这两个struct的len,cap,ptr都相同
	//因为ptr相同,所以操作的是同一个数组
	//从这个角度来看,其实数组的值是变了的

	fmt.Println(arr[3]) // 123 确实是变了

	//但是为什么切片还是0.0呢?
	//那是因为在M1中的切片跟main中的切片不是同一个,所以你刷新了M1中的切片
	//跟main中的切片有什么关系呢
}
func M1(c []int) {
	c = append(c, 123) //刷新了切片,为什么main中的打印还是0.0
}

那么怎么解决呢?我就想传递给函数一个切片,函数内切片的改动,函数外可以感知到,那么就用切片嘛,上代码:

func main() {
	arr := [5]int{}
	as := arr[1:3]
	as2 := as
	M2(&as)             //解决M2内外struct会拷贝的问题,换成切片指针,内外操作同一个切片就可以了
	fmt.Println(as)     //0.0.123
	fmt.Println(as2)    //0.0 函数内的赋值操作也是一次拷贝,as拷贝了一份给了as2 所以as2的切片还是旧的状态
	fmt.Println(arr[3]) //123 同样是修改了的(大家可以试试在M2内多新增几个元素,
					    // 让append帮你扩容,帮你去创建一个新的数组,再来打印一下arr[3]看看是个什么结果,换汤不换药,我们上文都解释过了)
}
func M2(c *[]int) {
	*c = append(*c, 123)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值