golang随笔之slice+append的陷阱

简介: golang随笔之slice+append的陷阱

问题初探

package main
import "fmt"
func main() {
  s1 := []int{1, 2}
  s2 := s1
  s2 = append(s2, 3)
  Test1(s1)
  Test1(s2)
  fmt.Println(s1, s2)
}
func Test1(s []int) {
  s = append(s, 0)
  for i := range s {
    s[i]++
  }
}

A:[2,3] [2,3,4]

B:[1,2] [1,2,3]

C:[1,2] [2,3,4]

D:[2,3,1] [2,3,4,1]


思考

今天看书看到这一题,一看,这肯定选D嘛,一翻答案,选C,离谱!!

知道s2 = append(s2, 3)这一句话会因为容量不够而发送扩容导致s1和s2的底层不一样,那传入Test1的时候,不是说好slice传递是引用吗,那么s = append(s, 0)扩容换底层array后,s1和Test1的s,他们是引用关系,那他们还是相同的一个slice吗?结果是,如果发生扩容了,那么就相当于值传递,那这样解释的话,s1是能说得通,那s2呢,s2在Test1中并没有发生扩容,为什么s2不是2,3,4,1?来看下面这个例子

package main
import "fmt"
func main() {
  s3:=make([]int,2,10)
  fmt.Println(s3)
  Test2(s3)
  fmt.Println(s3)
  s4:=s3[0:10]
  fmt.Println(s4)
}
func Test2(s []int){
  s=append(s,6)
  s=append(s,6)
  s=append(s,6)
  fmt.Println(s)
}
[0 0]
[0 0 6 6 6]
[0 0]
[0 0 6 6 6 0 0 0 0 0]

我们发现在没有发生扩容的情况下,s3的底层数组,和函数内的s一样,那么为什么s3没有变成006666呢?

解析

将一个切片作为函数参数传递给函数时,其实采用的是值传递,因此传递给函数的参数其实是切片结构体的值拷贝

type SliceHeader struct {
    Data uintptr
    Len int
    Cap int
}

我们以值传递的方式传递切片结构体的时候,同时也是传递了Len和Cap的值拷贝,因为这两个成员并不是指针,因此,当函数返回时,原切片结构体的Len和Cap并没有改变。

那如果想在函数中用append怎么办,这个时候就要传slice的指针了,如下面代码

package main
import "fmt"
func main() {
  arr := []int{1, 2, 3, 4}
  fmt.Println(arr)
  Test1(&arr)
  fmt.Println(arr)
}
func Test1(s *[]int) {
  *s = append(*s, 5)
  fmt.Println(*s)
}
[1 2 3 4]
[1 2 3 4 5]
[1 2 3 4 5]

Go中没有引用传递

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.

文档地址:https://golang.org/ref/spec#Calls

不能单纯因为函数内部的修改可以反馈到外面就认为是传递的引用。

我说怎么有的书上写slice map func的引用转递,有的地方说go都是值传递,看来还是没有说的透彻。

记住,go中全部都是值传递。


目录
相关文章
|
7月前
|
存储 Go
Golang底层原理剖析之slice类型与扩容机制
Golang底层原理剖析之slice类型与扩容机制
81 0
|
2月前
|
存储 缓存 测试技术
golang slice相关常见的性能优化手段
【10月更文挑战第23天】本文介绍了 Go 语言中切片使用的四个优化技巧:预分配容量、减少中间切片的创建、利用切片的复用特性和合理使用 `copy` 函数。通过这些方法,可以有效提高程序性能,减少不必要的内存分配和数据复制操作。每个技巧都附有详细的原理说明和代码示例,帮助开发者更好地理解和应用。
|
3月前
|
Go
Golang语言之切片(slice)快速入门篇
这篇文章是关于Go语言中切片(slice)的快速入门教程,详细介绍了切片的概念、定义方式、遍历、扩容机制、使用注意事项以及相关练习题。
40 5
|
6月前
|
Go 索引
GOLANG SLICE 的底层实现
GOLANG SLICE 的底层实现
|
存储 大数据 Go
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
108 0
golang踩坑 1.slice传参和for range赋值
golang踩坑 1.slice传参和for range赋值
|
编译器 Go
golang slice的扩容给你整明白的
golang slice的扩容给你整明白的
|
运维 Go
学习golang(5) 初探:go 数组/slice 的基本使用
学习golang(5) 初探:go 数组/slice 的基本使用
127 0
|
存储 Java Go
【Golang之路】——slice总结
【Golang之路】——slice总结
164 0
【Golang之路】——slice总结