发布网友 发布时间:2024-09-29 22:16
共1个回答
热心网友 时间:2024-10-10 02:47
原理Go语言中的切片事实上是一个结构体,其运行时结构如下:
?type?slice?struct?{?????array?unsafe.Pointer?????len???int?????cap???int?}这一点非常重要,这也就意味着,将切片作为函数参数时,其传递机制与结构体传递机制一样,都是值传递,也即传递的是原切片的拷贝。
另外一个非常重要的点就是,切片结构体中的array是一个指针,意味着array的值是底层数组的地址,通过函数传参后,这个值依然没有改变。
因此可以看到,当把切片作为函数参数传递时,在函数中对切片进行某些修改操作,会影响到函数外的原始切片。
看个栗子先看一段代码:
?func?main()?{?????var?arr?=?[]int{1,?2,?3,?4,?5}?????fmt.Printf("arr?pointer:?%p\n",?&arr)?????test(arr)?????fmt.Printf("arr:?%v\n",?arr)?}???func?test(data?[]int)?{?????fmt.Printf("data?pointer:?%p\n",?&data)?????data[0]?=?100?}问题:
请问上述代码中arr pointer:与data pointer:的输出是相同的吗?
请问上述代码中arr:打印的结果是多少?也即函数中对切片的修改会不会影响到函数外的原切片?
答案:
输出中arr pointer:与data pointer:的值不相同,这很好理解,这是因为切片是值传递,传递给函数的时候,重新创建了一个新的切片结构体,那么二者的地址当然不一样了。
打印的结果是arr: [100 2 3 4 5],也就证明了对切片的修改会影响到原切片。也正是这个原因导致很多人误以为切片作为函数参数时是引用传递,其实这种理解是错误的。
再看个栗子这个栗子与上面的栗子唯一不同的是,在函数体中对切片增加了一个append操作
?func?main()?{?????var?arr?=?[]int{1,?2,?3,?4,?5}?????fmt.Printf("arr?pointer:?%p\n",?&arr)?????test(arr)?????fmt.Printf("arr:?%v\n",?arr)?}??func?test(data?[]int)?{?????fmt.Printf("data?pointer:?%p\n",?&data)?????data?=?append(data,?100)?????data[0]?=?100?}那么问题来了:
此时arr:打印的结果是什么?
答案:打印的是arr: [1 2 3 4 5],如果理解了上面阐述的原理,那么也很好理解这个答案。这是因为通过append函数扩充一个元素时,由于原切片的容量不足,导致底层数组需要扩容,而扩容后的底层数组的地址改变了,因此函数中的data的结构体中的array值改变了,而后的data[0] = 100语句操作的已经是新的底层数组了,因此也就与函数外的原切片中指向的底层数组不是同一个了。
最后最后,我想说的是,如果你确定编写的函数需要将切片的修改影响到函数外的原始切片,那么你的函数参数应该使用指针。
希望现在你可以清楚地回答下面的问题了
?//?代码一?func?main()?{?????var?arr?=?[]int{1,?2,?3,?4,?5}?????fmt.Printf("arr?pointer:?%p\n",?&arr)?????test(&arr)?????fmt.Printf("arr:?%v\n",?arr)?}??func?test(data?*[]int)?{?????fmt.Printf("data?pointer:?%p\n",?data)?????(*data)[0]?=?100?}?//?代码二?func?main()?{?????var?arr?=?[]int{1,?2,?3,?4,?5}?????fmt.Printf("arr?pointer:?%p\n",?&arr)?????test(&arr)?????fmt.Printf("arr:?%v\n",?arr)?}??func?test(data?*[]int)?{?????fmt.Printf("data?pointer:?%p\n",?data)?????*data?=?append(*data,?100)?????(*data)[0]?=?100?}问题:
请问上述代码中arr pointer:与data pointer:的输出是相同的吗?
请问上述代码中arr:打印的结果是多少?
彩蛋附加一个小问题:
?var?arr?=?[]int{1,?2,?3,?4,?5}?fmt.Printf("arr?pointer:?%p\n",?arr)?fmt.Printf("arr?pointer:?%p\n",?&arr)上面??代码中两行输出语句的区别是什么?
答案:对于切片来说,使用%p格式化输出时,如果前面不加取地址符,那么打印的是切片中第一个元素的地址;如果前面加上取地址符&,那么打印的是该切片的地址。
原文:https://juejin.cn/post/7096054655511658526