golang函数传参——值传递理解
做了五年的go开发,却并没有什么成长,都停留在了业务层面了。一直以为golang中函数传参,如果传的是引用类型,则是以引用传递,造成这样的误解,实在也不能怪我。我们来看一个例子,众所周知,slice是个引用类型,我们以slice为例。
package main
import "fmt"
func main() {strSlice := make([]string, 0,10)strSlice = append(strSlice, "初始值")//打印一下没有在函数内部修改的初始情况fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0])//在函数内部修改初始slice内容,再打印change(strSlice)fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0])
}
func change(str []string){fmt.Println("函数传参地址:",&str[0])str[0]="改掉这个内容"
}
猜猜,打印出来的会是什么结果?
不管你是怎么看,如果只有这么一个案例,就很容易产生误解。打印的函数参数的地址和外部slice的地址是一致,并且在函数体内修改的值的确影响了slice的值,由此现象很容易得出是引用传递。
事实果真如此么?我们再来看一个案例。
package main
import "fmt"
func main() {strSlice := make([]string, 0,10)strSlice = append(strSlice, "初始值")//打印一下没有在函数内部修改的初始情况fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0])//在函数内部修改初始slice内容,再打印change(strSlice)fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0])
}
func change(str []string){fmt.Println("函数传参地址:",&str[0])str=append(str,"新增一个内容")
}
如果是引用传递,那么经过change函数追加了值的strSlice应该是能打印出追加的值。好了,我们直接看结果。

结果,很是出乎我的意料之外啊,竟然不是像我上面猜想的那样。由此至少可以得出一个结论,golang中函数的参数传递不是引用传递。那么,也就是说即使参数是引用类型,也是值传递,既然是值传递,第一个案例作何理解呢?第二个案例又如何理解呢?
我们看一下slice的底层结构。
//go 1.20.3 path: /src/runtime/slice.go
type slice struct {array unsafe.Pointerlen intcap int
}
array 是一个指向底层数组的指针,这个数组存储着切片中的元素。len 表示切片的长度,即切片中元素的数量。cap 表示切片的容量,即切片底层数组中可用的元素数量。golang的函数传参都是值传递,即使传递的是引用类型,也是对应引用类型的地址拷贝。因此,第一个案例中,实际上是把指向底层数组的指针的地址拷贝生成一个副本传到了函数体中,所以,第一个案例中修改了0xc00006c0a0地址里的内容会引发外面的参数发生变化。这个我们可以做个案例测试一下。
package main
import "fmt"
func main() {strSlice := make([]string, 0,10)strSlice = append(strSlice, "初始值")//打印一下没有在函数内部修改的初始情况fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0])//在函数内部修改初始slice内容,再打印change(strSlice)fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0])
}
func change(str []string){fmt.Println("函数传参地址:",&str[0])for i:=0;i<10;i++{str = append(str, fmt.Sprint(i))}fmt.Println("扩容之后的地址:",&str[0])str[0]="改掉这个内容"
}
我们知道,当slice发生扩容,runtime会开辟一块新的内存地址把内容拷贝到新的地址指向的内存中,那么,我们可以测试一下,当slice发生扩容,再修改内容,就不会影响原来的参数。
实际结果,验证了我们的猜想,扩容之后,开辟新的内存地址来存放内容,因此,再修改这个参数也不会影响外部参数。
可是这个依然没有解除掉第二个案例——没有扩容时,函数内append之后外部参数打印结果和预期不符的疑惑。实际上并不矛盾,因为,slice结构中有一个变量len,这个表示slice中元素的数量,用大白话来理解就是可见的元素,传参的过程中,不仅拷贝了地址,还拷贝了len和cap,因此,虽然形参中的len发生了变化,但是并不影响实参的len。画个内存示意图来理解一下。

package main
import "fmt"
func main() {strSlice := make([]string, 0,10)strSlice = append(strSlice, "初始值")//打印一下没有在函数内部修改的初始情况fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0],"len=",len(strSlice))//在函数内部修改初始slice内容,再打印change(strSlice)fmt.Println("strSlice:",strSlice,"strSlice地址:",&strSlice[0],"len=",len(strSlice))
}
func change(str []string){fmt.Println("函数传参地址:",&str[0])str=append(str,"新增一个内容")fmt.Println("形参str长度:",len(str))
}

直接看结果,果然验证了我们上面的猜想。
至此,函数值传递的探究到此结束。
相关文章:
golang函数传参——值传递理解
做了五年的go开发,却并没有什么成长,都停留在了业务层面了。一直以为golang中函数传参,如果传的是引用类型,则是以引用传递,造成这样的误解,实在也不能怪我。我们来看一个例子,众所周知…...
Liunx环境下git的详细使用(gitee版)
Liunx环境下git的详细使用(gitee版) 1.git是什么2.git操作2.1在gitee创建一个仓库2.2.gitignore2.3.git 3.git三板斧3.1add3.2 commit3.3push 4.git其他命令4.1查看当前仓库状态4.2查看提交日志4.3修改git里面文件名称4.4删除文件4.5修改远端仓库内容 1.…...
exoplayer的使用-2,与flutter相互通信
上一次解决的问题是ac3这些exoplayer本身不支持音频,添加了ffmpeg扩展实现软解码. 另一个问题是flutter端的内置字幕显示不了,也不打算再继续探讨了,换成native实现播放器.由于主项目是flutter的,所以涉及到了相互通信. 当前文章主要涉及到flutter与native相互通信功能 通信有…...
【基础类】—类型转换
一、数据类型 原始类型 Boolean、Null、Undefined、Number、String、Symbol 对象 Object 二、显示类型转换 Number函数, Number(param) 1-1. param 是 原始类型 时 数值:转换后还是原来的值 字符串:如果可以被解析…...
【云原生】 一文了解docker中的网络
文章目录 1.在Docker中,网络的实现方式是通过使用不同的网络驱动程序来实现的。2.桥接网络是默认网络。3.主机网络模式是不使用网络隔离的一种特殊模式。4.另一个有用的网络驱动程序是覆盖网络。5.最后,MACVLAN网络驱动程序允许多个容器与宿主机网卡的单独MAC地址和IP地址之间…...
嵌入式开发学习(STC51-15-红外遥控)
内容 使用外部中断功能,使按下红外遥控器,将对应键值编码数据解码后通过数码管显示 红外遥控介绍 红外线简介 人的眼睛能看到的可见光按波长从长到短排列,依次为红、橙、黄、绿、青、蓝、紫; 其中红光的波长范围为 0.62&…...
代码编辑器实践之vue-codemirror使用
前言 程序员用到IDE次数比较频繁,比如vscode、idea等,这些都是市场上比较流行的代码编辑器,拥有非常全面的功能。但是有时候在项目开发上也会用到代码编辑器,比如复杂的Array<Object>输入,或者需要用到用户交互…...
Mapstruct
1. Mapstruct介绍 1. 实体类之间对象映射中间件 2. 实体类相同结构属性自动对象映射 3. 实体类不同结构属性可以手动配置对象映射 2. Mapstruct基本使用 1. 定义一个接口或抽象类加Mapper(componentModel “spring”);spring:可以spring框架注入 2. 定义…...
初阶C语言——特别详细地介绍函数
系列文章目录 第一章 “C“浒传——初识C语言(更适合初学者体质哦!) 第二章 详细认识分支语句和循环语句以及他们的易错点 第三章 初阶C语言——特别详细地介绍函数 目录 系列文章目录 前言 一、函数是个什么鬼东西? 二、C语…...
pulsar-client-1-2 PulsarClient构造函数
前言 上文说到,PulsarClient通过链式调用构建,而在build()中调用了new PulsarClientImpl(conf),而Producer 本文通过解析构造函数,了解其主要结构。 // 创建PulsarClient PulsarClient client = PulsarClient.builder().serviceUrl("pulsar://localhost:6650")…...
原型链污染是什么
__proto__ 一个Foo类实例化出来的foo对象,可以通过foo.__proto__属性来访问Foo类的原型,也就是说: foo.__proto__ Foo.prototype 对象 构造函数 所以,总结一下: prototype是一个类的属性,所有…...
java之石头迷阵单击游戏、继承、接口、窗体、事件、组件、按钮、图片
文章目录 前言主方法实现类 前言 主方法和实现类在同一个包中。 主方法 package PSortGames;public class Main {public static void main(String[] args) {new MainFrame();} }实现类 package PSortGames;import javax.swing.*; import java.awt.event.KeyEvent; import jav…...
Debian 系列 Linux 的静态 DNS 、gateway 、IP 设置和网络重启
文章目录 DNS 设置DNS 配置文件DNS 配置文件内容 gateway、IP 设置网络适配器配置文件网络适配器配置文件内容 网络重启 各个 Linux 发行版的网络设置有很大不同。根据最近对 Debian 系列(含 Debian 12、基于 Debian 12 的Proxmox 8.0-2 以及基于Debian 11 的 openm…...
IP路由基础+OSPF 基础
IP路由 RIB与FIB RIB:Routing Information Base,路由信息库 ,路由器的控制平面 FIB:Forwarding Information Base,转发信息库,路由器的数据平面 路由信息库主要是记录直连路由以及协议宣告的路由信息&am…...
window远程连接Linux
Linux启用ssh服务 管理员权限进入root 安装 OpenSSH 服务器软件包 对于 Debian、Ubuntu 或基于这些发行版的系统,可以运行以下命令安装: sudo apt update sudo apt install openssh-server对于基于 Red Hat、CentOS 或 Fedora 的系统,可以…...
MyBatis 查询数据库之二(增、删、改、查操作)
目录 1. 配置打印 MyBatis 执行的SQL 2. 查询操作 2.1 通过用户 ID 查询用户信息、查询所有用户信息 (1) Mapper 接口 (2)UserMapper.xml 查询所有用户的具体实现 SQL (3)进行单元测试 3. 增加操作 3.1 在 mapper(interface)里面添加增加方法的声…...
unraid docker桥接模式打不开页面,主机模式正常
unraid 80x86版filebrowser,一次掉电后,重启出现权限问题,而且filebrowser的核显驱动不支持amd的VA-API 因为用不上核显驱动,解压缩功能也用不上,官方版本的filebrowser还小巧一些,18m左右 安装的时候总是…...
第七章:进程间通信(IPC)——构成进程间通信的信道方案
系列文章目录 文章目录 系列文章目录前言进程间通信介绍进程间通信目的进程间通信发展进程间通信分类进程通信的原理 管道什么是管道pipe管道通信特点简单设计 命名管道什么是命名管道mkfifostrcmp/strncasecmpunlinkgetch简单设计 共享内存什么是共享内存shmget/ftokipcsshmct…...
部分常用CSS样式
目录 1.字体样式 2.文本样式 3.鼠标样式 cursor 4.背景样式 5.列表样式 6.CSS伪类 7.盒子模型 1.字体样式 font-family 字体类型:隶书” “楷体” font-size 字体大小:像素px font-weight 字体粗细:bold 定义粗体字…...
思科单臂路由、lacp链路聚合、NAT实验
实验拓扑图: 实验目的: 如图所示配置相应IP地址和VLAN,并通过在AR1上配置单臂路由,实现VLAN10和VLAN20的主机能够在VLAN间通信;在SW1和SW2的三条链路实施链路聚合,使用静态LACP模式,使一条链…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
