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

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

问题情况:

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

    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 有任何影响,结果自然是这样的

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

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

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

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

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

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

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

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

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

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

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

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

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

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

传入切片的地址

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

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

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

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

感谢阅读,欢迎交流,点个赞,关注一波 再走吧

欢迎点赞,关注,收藏

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

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

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

文中提到的技术点,感兴趣的可以查看这些文章:

  • 你以为传切片就是传引用了吗?
  • 你真的知道 GO 中 nil 代表什么吗?
  • GO 中 slice 的实现原理
  • 微服务线上问题排查困难?不知道问题出在哪一环?那是你还不会分布式链路追踪
  • k8s 服务升级为啥 pod 会部署到我们不期望的节点上??看来你还不懂污点和容忍度
    可以进入地址进行体验和学习:https://xxetb.xet.tech/s/3lucCI

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI 更新 ChatGPT:支持图片和语音输入【附点评】

一、消息正文 9月25日消息,近日OpenAI宣布其对话AI系统ChatGPT进行升级,添加了语音输入和图像处理两个新功能。据OpenAI透露,这些新功能将在未来两周内面向ChatGPT Plus付费用户推出,免费用户也将很快可以使用这些新功能。这标志着ChatGPT继续朝着多模态交互的方向发展,为用户提…

Lnmp架构之mysql数据库实战2

4、mysql组复制集群 一主多从的请求通常是读的请求高于写 ,但是如果写的请求很高,要求每个节点都可以进行读写,这时分布式必须通过(多组模式)集群的方式进行横向扩容。 组复制对节点的数据一致性要求非常高&#xff…

EcmaScript标准-导入与导出-js

ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,所以它…

Cannot download sources

问题 Swagger的相关包,没法看到注释;源码也下载不了,会报下面的错误。 解决办法是,通过maven,重新下载jar包。 报错 Cannot download sources Sources not found for: io.swagger.core.v3:swagger-annotations:2.2.…

主从复制是怎么实现的?

单机模式的缺点 Redis虽然有持久化技术保证Redis奔溃后重启可以恢复数据,但是,单机模式下还是存在两方面问题。一方面Redis一旦宕机,数据恢复需要一定的时间,这段时间内,都不能接收和处理请求;另一方面&am…

【教程】Ubuntu自动查看有哪些用户名与密码相同的账户,并统一修改密码

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 目录 背景说明 开始操作 修改密码 背景说明 有些用户为了图方便或者初始创建用户默认设置等原因,会将密码设置为与用户名相同,但这就使得非常不安全。甚至如果该用户具有sudo权限&#…

云原生Kubernetes:Pod控制器

目录 一、理论 1.Pod控制器 2.Deployment 控制器 3.SatefulSet 控制器 4.DaemonSet 控制器 5.Job 控制器 6.CronJob 控制器 二、实验 1.Deployment 控制器 2.SatefulSet 控制器 3.DaemonSet 控制器 4.Job 控制器 5.CronJob 控制器 三、问题 1. showmount -e 报错…

HTML5福利篇--使用Canvas画图

目录 一.Canvas元素 1.Canvas元素定义 2.使用JavaScript获取页面中的Canvas对象 二.绘制图形 1.绘制直线 2.绘制矩形 (1)rect() (2)strokeRect() (3)fillRect()和clearRect()函数 3.绘制圆弧 4.…

layui 树状控件tree优化

先上效果图: 我选的组件是这个: 动态渲染完后,分别在窗体加载完成,节点点击事件分别加入js: //侧边栏图标替换//layui-icon-subtraction$(function () {$(".layui-icon-file").addClass("backs&quo…

3 OpenCV两张图片实现稀疏点云的生成

前文: 1 基于SIFT图像特征识别的匹配方法比较与实现 2 OpenCV实现的F矩阵RANSAC原理与实践 1 E矩阵 1.1 由F到E E K T ∗ F ∗ K E K^T * F * K EKT∗F∗K E 矩阵可以直接通过之前算好的 F 矩阵与相机内参 K 矩阵获得 Mat E K.t() * F * K;相机内参获得的方式…

2.物联网射频识别,RFID通信原理,RFID读写器与标签无线交互方式、数据反馈方式,RFID调制与解调、编码方式,不同RFID标签与读写器

一。RFID无线识别的原理 1.RFID系统无线通信基本原理 如下图所示,左边是读写器(刷卡器),右边是标签(卡),中间通过无线通信方式。 标签:(卡) 读写器&#xff…

Arthas学习(1)

1.Arthas作用 Arthas是Alibaba开源的Java诊断工具。 作用:当遇到以下类似问题时,可以帮助我们解决: 1.这个类从哪个jar包加载的?为什么会报各种类相关的Exception? 2.我改的代码为什么没有执行到?难道是我没提交&am…