【切片】基础不扎实引发的问题

简介: 本次文章主要是来聊聊关于切片传值需要注意的问题,如果不小心,则很容易引发线上问题,如果不够理解,可能会出现奇奇怪怪的现象

问题情况:

小 A 负责一个模块功能的实现,在调试代码的时候可能不仔细,部署到线上环境时发现在现有策略列表上追加新的策略时,总是无法生效,这是为什么呢?

追查代码后发现问题出在关于切片的使用上出了认知偏差,小 A 认为 golang 中,传切片就是传引用,因此写出了这样的代码片段


func xxxFunc(sli []int ,newSli []int) {
   // ... 省略部分代码
   sli = append(sli, newSli...)
   // ... 省略部分代码
   return 
}

想表达的意思是:

传入的 sli 切片属于旧切片,期望在 sli 切片上追加 newSli 中的元素,最终期望得到的 sli 里面是包含 newSli 元素的

然而,对于 Golang 中切片 slice 有一定了解的 xdm 就很清楚,这样写其实并没有什么实际作用,在 Golang 中传参都是传值而不是传地址

因此此处传入的 sli 切片,也仅仅是一个拷贝而已,在 xxxFunc 函数中的 sli 切片被修改了,实际上是不会影响函数外部的 sli 的

那么对于切片此处做几个阐述

首先强调几点关于切片的注意事项

  1. Golang 中的函数参数,都是传值,不是传地址
  2. 对于切片自身的底层数据结构,我们可以通过索引的方式拿到底层数组的地址,并修改其地址上的值,例如 sli[2] = "hello",这是可以直接修改
  3. 如果传入的切片,期望实参也能够被改变的话,那么就需要想办法修改切片的底层数组
  1. 通过传切片的地址,也就是传指针的方式
  2. 在函数中,去索引切片的底层数组地址,进行修改数据

案例 1 遍历的时候修改

通过 value 修改切片值 - 不靠谱

我们给出一个切片 var mySlice = []int{7, 8, 9} ,并编写如下几个函数来查看是否会对原有切片数据有影响

func main() {
   log.SetFlags(log.Lshortfile)
   var mySlice = []int{7, 8, 9}
   log.Printf("mySlice == %+v", mySlice)
   log.Println("---------------------------------------------")
   mySliceDemo := testDemo(mySlice)
   log.Printf("mySlice == %+v ,mySliceDemo == %+v", mySlice, mySliceDemo)
   log.Printf("mySlice == %p ,mySliceDemo == %p", &mySlice, &mySliceDemo)
}
func testDemo(sli []int) []int {
   for _, value := range sli {
      value *= 2
   }
   return sli
}

给 testDemo 传入 mySlice 切片,在函数内部通过 for...range 的方式去修改切片内元素的值,然而代码中的 value 仍然是一个拷贝,他并不会真的对外部的 mySlice 有任何影响,结果自然是这样的

image.png

可以通过修改切片索引上的值

当然如果我们这样写,去找到索引对应的底层数组的地址,再修改其地址上的值,是可行的

func testDemo2(sli []int) []int {
   for index, _ := range sli {
      sli[index] *= 2
   }
   return sli
}

image.png

自然通过指针的方式仍然可以

传入的这个指针,实际上也是一个拷贝,只不过拷贝的是这个指针,也就是指针自身的地址不一样,但是他们指向的底层数组是一样的,因此可以直接修改

这种修改的方式,也是去修改地址上的值,因此有效


func testDemo3(sli *[]int)  {
   for index, _ := range *sli {
      (*sli)[index] *= 2
   }
   return
}

结果自然 ok,原有 mySlice 的地址也是没有发生变化的,只是值发生了变化

image.png

案例 2 使用 append 会有什么不同

那么如果是在子函数里面使用 append 追加数据,是否会有不同的效果?

func appendDemo(sli []int) []int {
   sli = append(sli, 999)
   return sli
}

image.png

实际上,此处传入的仍然是 mySlice 的拷贝,appendDemo 中使用 append,也是基于拷贝后的值来进行数据追加

哪怕是遇到切片扩容的情况,也仅仅是对于函数内的拷贝副本来进行扩容和变化,例如这样

func appendDemo3(sli []int)[]int{
   sli = append(sli, []int{3,4}...)
   return sli
}

image.png

传入切片的地址

在使用 append 的情况, 向函数参数中传入切片的指针,此处对于函数来说,仍然是一个副本,只不过这个副本是指针,指向的底层数组仍然是和 mySlice 是一样的,因此可以通过这个拷贝的指针去修改实际底层数组的值

func appendDemo2(sli *[]int){
   *sli = append(*sli, []int{1000,10001}...)
   return
}

image.png

可以看到使用指针的方式,处理起来还是妥妥的,在 appendDemo2 中实际修改了 mySlice 的值,且也是我们所期望的

至此,对于文章开头问题的解决方式,xdm 心中都有数了吧,那就不能再犯了吧,希望能够给你带来帮助

欢迎点赞,关注,收藏

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

image.png

好了,本次就到这里

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

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

相关文章
|
5月前
|
存储 C语言 索引
掌握多维数组,让你的C语言编程技能暴涨!
掌握多维数组,让你的C语言编程技能暴涨!
|
2月前
|
数据处理 索引 Python
NumPy 数组操作:和普通操作相较,到底蕴含着怎样令人费解的独特魅力?
【8月更文挑战第19天】NumPy是Python科学计算核心库,提供高效数组操作。不同于Python列表直接列举创建,NumPy用`np.array()`创建数组。两者都支持索引和切片,但NumPy性能更优。数学运算方面,NumPy支持简洁的向量化操作,如`my_array * 2`,无需循环。NumPy还简化了数组形状变换,如使用`reshape()`方法。此外,NumPy数组要求元素类型一致,提高了内存使用效率和计算速度。这些特点使NumPy在科学计算和数据分析中不可或缺。
29 0
|
3月前
|
Python
不容错过!Python中图的精妙表示与高效遍历策略,提升你的编程艺术感
【7月更文挑战第11天】在Python编程中,图以邻接表或邻接矩阵表示,前者节省空间,后者利于查询连接。通过字典实现邻接表,二维列表构建邻接矩阵。图的遍历包括深度优先搜索(DFS)和广度优先搜索(BFS)。DFS使用递归,BFS借助队列。这些基础技巧对于解决复杂数据关系问题,如社交网络分析或迷宫求解,至关重要,能提升编程艺术。
56 5
|
5月前
|
存储 搜索推荐 算法
数据结构奇妙旅程之七大排序
数据结构奇妙旅程之七大排序
|
5月前
|
存储 算法 安全
【Java编程进阶之路 02】深入探索:红黑树如何重塑哈希表的性能边界
JDK 1.8之后,HashMap引入红黑树来优化性能,当链表长度超过阈值(默认为8)时,链表会转换为红黑树,从而提高高冲突时的查询效率。同时,HashMap也采用了扰动函数来增加哈希值的随机性,使键值对更均匀分布,提升性能。
74 0
|
5月前
|
存储 传感器 机器学习/深度学习
Java数组全套深入探究——进阶知识阶段6、三维数组以及更多维度数组的概念和用法
Java数组全套深入探究——进阶知识阶段6、三维数组以及更多维度数组的概念和用法
103 0
|
5月前
|
存储 算法
【编码狂想】LeetCode 字符串和数组篇:挑战算法精髓,深化程序设计基础
【编码狂想】LeetCode 字符串和数组篇:挑战算法精髓,深化程序设计基础
54 0
|
10月前
|
Kubernetes Cloud Native Go
【切片】基础不扎实引发的问题
【切片】基础不扎实引发的问题
|
存储 机器学习/深度学习 搜索推荐
韵动代码:C++数组实践与应用之路 1
韵动代码:C++数组实践与应用之路
|
存储 算法 C++
韵动代码:C++数组实践与应用之路2
韵动代码:C++数组实践与应用之路