你以为传切片就是传引用了吗?

简介: xdm ,我们在写 golang 的时候,引用和传值傻傻分不清,就例如我们传 切片 的时候,你能分清楚你传的切片是传值还是传引用呢?

xdm ,我们在写 golang 的时候,引用和传值傻傻分不清,就例如我们传 切片 的时候,你能分清楚你传的切片是传值还是传引用呢?

引用是什么?

引用就是给对象起另一个名字,引用类型引用另一种类型

引用并不是对象,相反的,它只是为一个已存在的对象所起的另外一个名字

我们在写 C++ 的时候,知道引用其实就是实际值的另外一个别名,有的会举例子说是一个门牌号的概念

你以为的 引用传递

写个例子,咱们创建一个 切片 s1,

  • s1 切片赋值 , 1, 2, 3, 4, 5 , 输出效果
  • 将 s1 传递给 myModify ,在函数中修改 第 3 个元素第 4 个元素的值
func main() {
  s1 := make([]int, 5)
  s1 = []int{ 1, 2, 3, 4, 5}
  fmt.Println("s1 = ", s1)
  myModify(s1)
  fmt.Println("myAdd result = ", s1)
}
func myModify(ss []int ) {
  ss[2] = 8
  ss[3] = 9
}

查看效果

# go run main.go
s1 =  [1 2 3 4 5]
myAdd result =  [1 2 8 9 5]

看到这里,你以为的引用传递是不是就是这个样子的,因为传入到函数中的切片,在函数中把传进来的切片的值修改,外面实际的切片对应的值也改变了

xdm , 看到这里你就下定论是不是不太好,咱们再来探究一下

确认还是引用吗?

咱们在上面的实现上面具体的打印出 切片的地址来看看效果,我们就稍微有点感觉了

func main() {
  s1 := make([]int, 5)
  s1 = []int{ 1, 2, 3, 4, 5}
  fmt.Printf("s1地址  =  %p , s1: %v \r\n",&s1, s1)
  myModify(s1)
  fmt.Printf("s1地址  =  %p , s1: %v \r\n",&s1, s1)
}
func myModify(ss []int ) {
  ss[2] = 8
  ss[3] = 9
  fmt.Printf("ss地址  =  %p , s1: %v \r\n",&ss, ss)
}


运行程序,看实际效果

# go run main.go
s1地址  =  0xc42000a060 , s1: [1 2 3 4 5]
ss地址  =  0xc42000a0a0 , s1: [1 2 8 9 5]
s1地址  =  0xc42000a060 , s1: [1 2 8 9 5]

这个时候,你还觉得咱们传递的是引用吗?这个做法还是引用传递吗?咱们要好好思考一下

s1切片的地址 是 0xc42000a060 ,ss 切片的地址是 0xc42000a0a0,这里很明显看出来 s1  和 ss 并不是同一个东西,因此,我们可以得出结论,切片作为函数参数,是值传递

可以为什么 s1 和 ss 打印出来的数据是一样的呢,而且修改了 ss 切片里面的元素为什么会影响到原来 s1 的切片呢?

开始探究

GO 里面的切面的底层是是一个结构体

type SliceHeader struct {
  Data uintptr
  Len  int
  Cap  int
}

对于上面的问题, s1 和 ss 变量,有各自变量的内存地址,但是这个变量下面对应的底层数组是对应的同一片空间,因此 s1 的变动会影响到 ss 切片的变动

对于这个我们也是很好验证的,来写一个例子,将上述的两个切片里面的每隔元素的地址都打印出来对比一下:

func main() {
  s1 := make([]int, 5)
  s1 = []int{1, 2, 3, 4, 5}
  fmt.Printf("s1地址  =  %p , s1: %v \r\n", &s1, s1)
  for k, v := range s1 {
    fmt.Printf("s1[%d]地址  =  %p , s1: %v \r\n", k, &s1[k], v)
  }
  myModify(s1)
  fmt.Printf("s1地址  =  %p , s1: %v \r\n", &s1, s1)
}
func myModify(ss []int) {
  ss[2] = 8
  ss[3] = 9
  fmt.Printf("ss地址  =  %p , s1: %v \r\n", &ss, ss)
  for k, v := range ss {
    fmt.Printf("ss[%d]地址  =  %p , s1: %v \r\n", k, &ss[k], v)
  }
}

查看效果

image.png

根据上述效果,我们可以看到,s1 和 ss ,只是自己变量的地址不同,但是自己指向的底层数组的内存完全相同

平时工作中,这里就需要注意了,你以为的你以为真不可能不是这样的!!


欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

image.png

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~

相关文章
|
8月前
浅学指针(2)数组函数传值调用
浅学指针(2)数组函数传值调用
|
2月前
|
存储 索引
Zig 数组和切片
Zig 数组和切片
30 0
|
8月前
|
存储 弹性计算 运维
字符串切片
【4月更文挑战第29天】
37 1
|
8月前
|
C语言
在引用数组元素时指针的运算
在引用数组元素时指针的运算
63 0
|
JavaScript
多个js对象数组合并成一个对象数组的解决方案
多个js对象数组合并成一个对象数组的解决方案
323 0
|
8月前
|
存储 C语言 索引
指针详解(const、指针运算、数组名的理解、传址调用和传值调用、一维数组的本质​)(一)
指针详解(const、指针运算、数组名的理解、传址调用和传值调用、一维数组的本质​)(一)
|
Cloud Native Go C++
你以为传切片就是传引用了吗?
你以为传切片就是传引用了吗?
【数组拷贝+二维数组遍历】
【数组拷贝+二维数组遍历】
59 0
|
存储 算法 编译器
学C的第十二天【深入了解数组:一维和二维数组的创建和初始化;一维和二维数组的使用;一维和二维数组在内存中的存储;数组越界;数组作为函数参数;冒泡排序(对数组名的理解)】-2
5.二维数组的使用 操作符 [ ] :下标引用操作符,它其实就是数组访问的操作符,使用两个[ ],访问行和列 二维数组的行和列都是从0开始的
|
Java C# C++
C#中的多维数组和交错数组
C#中有多维数组和交错数组,两者有什么区别呢! 直白些,多维数组每一行都是固定的,交错数组的每一行可以有不同的大小。 以二维的举例,二维数组就是m×n的矩阵,m行n列;而交错数组(又叫锯齿数组)有m行,但是每一行不一定是n列。Got it? 在这个意义上,C++和Java中的多维数组起始相当于C#中的交错数组,要使用多维数组,只需要保证每个维度的长度是相等的就OK了! 因为m×n的矩阵这样的多维数组比较常用,感觉C#中对两个进行了区分,提供了一些便利!
85 0