go切片实现原理

近日一直在学习golang,已经产出如下博客一篇

  • GO闭包实现原理(汇编级讲解)

引言

最近在使用go语言的切片时,出现了一些意料之外的情况,遂查询相关文档学习后写下此篇博客

正文

首先,我们思考,go在通过函数传递一个切片时,是通过引用传递的吗,还是通过值传递的呢(答案将会很意外的哦)

值传递?

首先,先看如下简单代码,将一个string类型的切片传入函数后经过修改,在使用append()函数对切片进行添加之后,在函数的外部进行打印后却能发现,在内部添加数据并没有影响function()函数外面的str

看起来像是值传递,让我们继续往下看

func function(str []string){str = append(str,"c","lua","c#")
}func main() {str := []string{"c++","java","golang"}function(str)fmt.Println(str)
}
[Running] go run "d:\goProject\src\learn\package main.go"
[c++ java golang][Done] exited with code=0 in 1.586 seconds

引用传递?

将一个string类型的切片传入函数后经过修改,修改后影响到了外面[]string切片

func function(str []string){str[1] = "python"
}func main() {str := []string{"c++","java","golang"}function(str)fmt.Println(str)
}
[Running] go run "d:\goProject\src\learn\package main.go"
[c++ python golang]

所以,go的切片是使用的引用传递吗?

no,请继续向下看

我们可以惊奇的发现,先对切片进行append追加,在进行修改后,在函数外面进行打印,修改居然失效了

func function(str []string){str = append(str,"c","lua","c#")str[1] = "python"
}func main() {str := []string{"c++","java","golang"}function(str)fmt.Println(str)
}
[Running] go run "d:\goProject\src\learn\package main.go"
[c++ java golang]

但如果我们先用make()函数先对[]string切片的容量进行设置,在进行赋值又能发现是有效的

func function(str []string) {str = append(str, "c", "lua", "c#")str[1] = "python"
}func main() {str := make([]string,3,10)str1 := []string{"c++", "java", "golang"}copy(str,str1)function(str)fmt.Print(str)
}
[Running] go run "d:\goProject\src\learn\package main.go"
[c++ python golang]
[Done] exited with code=0 in 1.858 seconds

到这里,读者的cpu是不是已经麻成一团了呢,哈哈,请先让我先对其中切片的原理进行讲解后,再回头来看,相信一定能看懂

原理解析

首先,切片类型在编译时期会生成一个结构体

  • array:相当于一个c语言的数组指针,指向切片的实际内存区域
  • len:切片的实际使用大小
  • cap:切片当前能够容纳的最大数量
type splice struct {array    unsafe.Pointerlen      intcap		 int
}

如下图所示

而在我们将切片通过函数传入时候,go直接对这个结构体进行了一次拷贝,也就是说,拷贝的是这个结构体的值,而不是真正的数组,如下图所示,拷贝是一次浅拷贝,两个结构体指针指向同一个底层的数组

image-20231226211810026

所以,当我们没有使用make()函数生成切片类型,并且设置切片的cap容量时候:

go就会对底层的数组进行一次扩容,此时传入函数的切片的array就会指向一块新的内存,如下图所示,故修改无用

image-20231226211910073

然而,如果我们使用make()生成切片,并且设置了cap,那么就会发生如下事情

假设len==3,cap==10

  1. 切片传入函数后添加3条数据,此时函数内的切片len==6,cap==10
  2. 由于传入时候,传入的splice结构体是值传递,所以,函数外的splice结构体len==3,cap==10,也就是说len变量并没有被修改,但是对于[0,len]这个区间内的参数的修改是可见的,然而,由于go有着比较严格的内存安全检查,如果我们直接对[3,6]这个区间的内存进行访问,go会提示运行时错误

实测

接下来我们进行实测,通过一点类似于c语言指针的骚操作 ,绕过go的安全检查,验证我们理论的正确性

  1. 首先使用make()生成切片,设置len==3,cap==10
  2. 使用copy()函数,将切片前三个string变量进行赋值
  3. 将切片通过函数传递给function()函数,在function()函数内部进行追加以及修改
  4. function()函数返回后,通过类似于c语言指针的骚操作,绕过go的安全检查,访问到len[3:6]这个区间的内容
  5. 通过打印可以看见,如我们所想,在function()函数内的修改和添加都成功了
func function(str []string) {str = append(str, "c", "lua", "c#")str[1] = "python"
}func main() {str := make([]string,3,10)str1 := []string{"c++", "java", "golang"}copy(str,str1)function(str)//fmt.Print(str)for i := 0; i < 6; i++ {ptr := unsafe.Pointer(uintptr(unsafe.Pointer(&str[0])) + uintptr(i)*unsafe.Sizeof(str[0])) fmt.Printf("%s,", *(*string)(unsafe.Pointer(ptr)))}
}
[Running] go run "d:\goProject\src\learn\package main.go"
c++,python,golang,c,lua,c#,
[Done] exited with code=0 in 1.757 seconds

总结

  • 切片在底层是一个结构体,在进行赋值传递时候,是将该结构体进行浅拷贝
  • 切片就是相当于一个动态数组,容量足够时候直接添加,不够时候重新创建一个更大的数组,再将原本的数据移动到新的数组(经过个人测试:默认二倍扩容,大小超过512时候不在使用二倍扩容,转而使用其他算法)

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

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

相关文章

Transformer之多角度解读

Transformer 文章目录 Transformer  &#x1f449;引言&#x1f48e; 一、 自注意力机制 &#xff1a; 主要用于 长距离依赖捕捉和转换序列二、 Encoder&#xff1a;2.1 多头注意力机制&#xff1a;2.2 残差连接&#xff1a; 三、 Decoder&#xff1a;3.1 Decoder 多头注意力…

hive sql无法停止

排查流程 hive任务停止是调用org.apache.hive.jdbc.HiveStatement的close()方法实现的 其底层是委托给org.apache.hive.service.cli.thrift.TCLIService.Iface客户端实例来实现。 同时&#xff0c;通过JDK动态代理为其织入了synchronized同步机制&#xff1a;其底层是委托给…

申请公众号上限是多少

一般可以申请多少个公众号&#xff1f;公众号申请限额在过去几年内的经历了很多变化。对公众号申请限额进行调整是出于多种原因&#xff0c;确保公众号内容的质量和合规性。企业公众号的申请数量从50个到5个最后到2个&#xff0c;对于新媒体公司来说&#xff0c;这导致做不了公…

【XMU学科实践二】豆瓣爬虫实践

文章目录 分析豆瓣阅读网站具体步骤构造headersBeautiful soup中的定位函数find() 、find_all() 完整爬虫代码 叠甲&#xff1a;仅供学习。。 XMU的小朋友实在不会了可以参考我的思路&#xff0c;但还是建议自己敲一遍哈。 学科实践二还是挺有意思的&#xff01; 分析豆瓣阅读网…

什么是jwt

jwt是JSON Web Token&#xff0c;由3部分构成&#xff1a; 头部Header&#xff1a;头部包含了两部分&#xff0c;token 类型和采用的加密算法&#xff08;可为none&#xff0c;后端应限制加密算法&#xff0c;不以这里为准&#xff09;。 载荷Payload&#xff1a;这部分才是重要…

Compose UI 之 Small TopAppBar

Small 类型 TopAppBar AppBar 主要由2类&#xff0c;顶部 AppBar 和底部 AppBar。 顶部 AppBar&#xff1a;主要包含了标题&#xff0c;action菜单&#xff0c;导航菜单。底部 AppBar&#xff1a;典型地包含主要导航项。 顶部 AppBar 顶部 AppBar 包含了 4 中类型&#xff…

webhook详解

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 webhook简介 在当今高度连接的网络世界中,没有什么可以孤立地发挥最佳作用。完成一项任务(几乎)总是需要多个实体的参与。电子商务应用程序需要与支付系统通信,支付…

Python快速入门系列-2(Python的安装与环境设置)

第二章&#xff1a;Python的安装与环境设置 2.1 Python的下载与安装2.1.1 访问Python官网2.1.2 安装Python对于Windows用户对于macOS用户对于Linux用户 2.2 集成开发环境&#xff08;IDE&#xff09;的选择与设置2.2.1 PyCharm2.2.2 Visual Studio Code2.2.3 Jupyter Notebook2…

线性dp+中位数,POJ3666 Making the Grade

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 3666 -- Making the Grade (poj.org) 二、解题报告 1、思路分析 先不考虑…

Vessel - Linux hackthebox

#hard #runc #RE #Nodejs-SQLI Enumeration .git leak 使用 dumpall 下载 .git 打开 routes/index.js 可以看到网站使用 nodejs mysql 编写&#xff0c;且只有登录功能 router.post(/api/login, function(req, res) {let username req.body.username;let password req…

ROS2中launch编写及参数含义(xml、python)

ROS2系列文章目录 ROS2中nav_msgs/msg/Path 数据含义及使用 ROS2中std_msgs/msg/Header 数据含义及使用 ROS中TF变换详解 文章目录 ROS2系列文章目录ROS2中launch编写及参数含义&#xff08;xml、python&#xff09;一、ROS官方介绍二、实现案例1.编写主函数、CMakeLists.tx…

基于springboot实现图书推荐系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现图书馆推荐系统演示 摘要 时代的变化速度实在超出人类的所料&#xff0c;21世纪&#xff0c;计算机已经发展到各行各业&#xff0c;各个地区&#xff0c;它的载体媒介-计算机&#xff0c;大众称之为的电脑&#xff0c;是一种特高速的科学仪器&#xff0c;比…

实时智能应答3D数字人搭建

语音驱动口型的算法 先看效果&#xff1a; 你很快就可以帮得上我了 FACEGOOD 决定将语音驱动口型的算法技术正式开源&#xff0c;这是 AI 虚拟数字人的核心算法&#xff0c;技术开源后将大程度降低 AI 数字人的开发门槛。FACEGOOD是一家国际领先的3D基础软件开发商&#xff0c;…

VS2017 boost环境配置与报错解决

1、下载Boost 2、boost编译 将下载好的压缩包文件解压,我这里放到了D:\Qt文件夹内 按win键找到 vs2017(主要看你的vs是什么版本)的x86_x64兼容工具命令提示符 输入以下命令: 首先进入到解压的boost文件夹,复制一下前面解压的路径 cd D:\Qt\boost_1_78_0然后输入命令,…

Jmeter(二) - 从入门到精通 - 创建测试计划(Test Plan)(详解教程)

1.简介 上一篇中已经教你把JMeter的测试环境搭建起来了&#xff0c;那么这一篇我们就将JMeter启动起来&#xff0c;一睹其芳容&#xff0c;首先给大家介绍一下如何来创建一个测试计划&#xff08;Test Plan&#xff09;。 2.创建一个测试计划&#xff08;Test Plan&#xff09…

Java设计模式深度解析:掌握工厂模式,优化对象创建与管理

工厂模式是一种创建型设计模式&#xff0c;它提供了一种在不指定具体类的情况下创建对象的方法。在Java中&#xff0c;工厂模式可以通过接口和实现类来实现。比如我们建一个外形工厂&#xff0c;工厂提供对外的获取外形方法&#xff0c;传入不同的参数即可获取不同的外形。如图…

git命令行提交——github

1. 克隆仓库至本地 git clone 右键paste&#xff08;github仓库地址&#xff09; cd 仓库路径&#xff08;进入到仓库内部准备提交文件等操作&#xff09; 2. 查看main分支 git branch&#xff08;列出本地仓库中的所有分支&#xff09; 3. 创建新分支&#xff08;可省…

Linux 之五:权限管理(文件权限和用户管理)

1. 文件权限 在Linux系统中&#xff0c;文件权限是一个非常基础且重要的安全机制。它决定了用户和用户组对文件或目录的访问控制级别。 每个文件或目录都有一个包含9个字符的权限模式&#xff0c;这些字符分为三组&#xff0c;每组三个字符&#xff0c;分别对应文件所有者的权限…

GIS之深度学习08:安装GPU环境下的pytorch

环境&#xff1a; cuda&#xff1a;12.1.1 cudnn&#xff1a;12.x pytorch&#xff1a;2.2.0 torchvision&#xff1a;0.17.0 Python&#xff1a;3.8 操作系统&#xff1a;win &#xff08;本文安装一半才发现pytorch与cuda未对应&#xff0c;重新安装了cuda后才开始的&a…

RT-thread学习持续更新中(1)

标准版RTT移植 先不要选择nano版本&#xff0c;选标准版的 include包含头文件 applications 应用层的程序编写放里面 drivers驱动 libraries库 rtthread的内核 LOG_D打印信息 通过修改board.c的SystemClock_Config&#xff08;&#xff09;更改系统时钟 由于在创建工程向导…