首页>>后端>>Golang->Go切片作为函数参数时的一些思考

Go切片作为函数参数时的一些思考

时间:2023-12-01 本站 点击:0

原理

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Golang/5876.html