没错,Go 语言的函数参数没有引用传递方式
这篇文章想浅浅地讲解 Go 语言函数参数传递的值拷贝。
一句话观点
Go语言中所有传递都是值传递,严格来说并不存在引用传递的概念。传递指针只是传递指针的值,并不是引用传递,只不过通过指针可以间接修改变量的值,从而达到类似引用传递的效果。
值传递
值传递就是将参数的副本传递给函数,因此在函数内部修改参数的值,不会影响到原始变量的值。
func modifyValue(person Person) {
person.Name = "Alice"
}
func Test4(t *testing.T) {
person1 := Person{Name: "Bob"}
modifyValue(person1)
fmt.Println("值传递:", person1.Name) // 输出: 值传递: Bob
}
在这个例子中,modifyValue 函数接收 person 的副本,修改它的字段值并不会影响原来的 person。
引用传递
要实现引用传递,可以通过传递指针来实现。
func modifyReference(person *Person) {
person.Name = "Alice"
}
func Test4(t *testing.T) {
person2 := &Person{Name: "Bob"}
modifyReference(person2)
fmt.Println("引用传递:", person2.Name) // 输出: 引用传递: Alice
}
在这个例子中,modifyReference 函数接收的是 person 指针类型的副本,修改副本指向的值,会影响到原来的。
值传递例子
下面再看一个值传递的例子。恰好最近在读《Go 语言精进之路》这本书,书里有一段讲解的方法的本质问题,案例是下面这段代码:
type field struct{ name string }
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data1 := []*field{{"one"}, {"two"}, {"three"}}
for _, v := range data1 {
go v.print()
}
data2 := []field{{"four"}, {"five"}, {"six"}}
for _, v := range data2 {
go v.print()
}
time.Sleep(3 * time.Second)
}
我对上面的代码进行简单改造,改为单测方法,打印输出内存地址,改造的代码如下:
func (p *field) print() {
fmt.Printf("%p %s \n", p, p.name)
}
func Test1(t *testing.T) {
data1 := []*field{{"one"}, {"two"}, {"three"}}
for _, v := range data1 {
go (*field).print(v)
}
data2 := []field{{"four"}, {"five"}, {"six"}}
for _, v := range data2 {
go (*field).print(&v)
}
time.Sleep(3 * time.Second)
}
运行单测,看下结果:
0xc000026590 six
0xc000026590 six
0xc000026590 six
0xc000026540 two
0xc000026530 one
0xc000026550 three
如果你奇怪为什么第二段代码输出的全是 six,那么可以接着看下面的原因分析。
先要知道的是, for range 循环中会重复使用同一个变量 v 。
在第一段,data1 是一个指针数组([]*field),v 每次迭代时是指向数组中不同元素的指针。由于 v 是指针,每个元素的地址自然是不同的,因此打印出的地址不同,每个元素都可以打印出来。
在第二段,data2 是一个结构体数组([]field),而 v 是数组元素的副本。当你取 &v 时,每次获取的都是这个循环中的同一个变量的地址,所以 &v 的地址是相同的。但每次迭代时,这个地址指向的值会被更新为 data2 中当前元素的副本,在最后执行的时候,&v 执行的值就是 six。
如果想让第二段输出所有元素,可以每次迭代拷贝一个 v 副本,复制给变量 s,如下:
for _, v := range data2 {
s := v
go (*field).print(&s)
}
执行单测,输出结果如下:
0xc0000265d0 six
0xc000026590 four
0xc0000265b0 five
0xc000026540 two
0xc000026550 three
0xc000026530 one
在这个例子中,每次迭代都会创建新的变量 s,内存地址是不同的,所以可以全部输出。
Over!
没错,Go 语言的函数参数没有引用传递方式的更多相关文章
- c++函数参数类型-引用、指针、值
c++函数参数类型-引用.指针.值 https://www.cnblogs.com/lidabo/archive/2012/05/30/2525837.html
- c语言中函数参数入栈的顺序是什么?为什么
看到面试题C语言中函数参数的入栈顺序如何? 自己不知道,边上网找资料.下面是详细解释 #include <stdio.h> void foo(int x, int y, int z){ ...
- PHP函数参数的引用传递和值传递
函数的参数传递有两种方式 1,值传递 常见的 test($param) 方式就是值传递,在函数内部修改$param,不会影响外部变量$param的值 2,引用传递 参数是引用传递的方式,此时函数内部 ...
- C语言通过函数参数不能带出动态内存的例子。
实验结论:通过函数参数不能带出动态内存,函数参数虽然为指针,其实是在函数内部的临时变量,只是该指针的初始值是通过调用函数赋值的.C语言函数参数都是传值的. #include <stdio.h&g ...
- python中函数参数的引用方式
值传递和引用传递时C++中的概念,在python中函数参数的传递是变量指向的对象的物理内存地址!!! python不允许程序员选择采用传值还是传引用.Python参数传递采用的肯定是“传对象引用”的方 ...
- R语言plot函数参数合集
最近用R语言画图,plot 函数是用的最多的函数,而他的参数非常繁多,由此总结一下,以供后续方便查阅. plot(x, y = NULL, type = "p", xlim = N ...
- C语言函数参数的传递详解
一.三道考题 开讲之前,我先请你做三道题目.(嘿嘿,得先把你的头脑搞昏才行--唉呀,谁扔我鸡蛋?)考题一,程序代码如下:void Exchg1(int x, int y){ int tmp; ...
- C++基础--引用做函数参数
引用,简单粗暴的解释叫做别名,简单粗暴的例子就是,我是熊叫大雄,但是很多时候别人不叫我熊叫大雄,会叫我大雄,粤语地区朋友爱叫我阿雄,有人叫我雄,所以,熊叫大雄这个变量的值是我,雄.大雄.阿雄是熊叫大雄 ...
- 6 JavaScript函数&内置构造&函数提升&函数对象&箭头函数&函数参数&参数的值传递与对象传递
JavaScript函数:使用关键字function定义,也可以使用内置的JavaScript函数构造器定义 匿名函数: 函数表达式可以存储在变量中,并且该变量也可以作为函数使用. 实际上是匿名函数. ...
- C++ const修饰函数、函数参数、函数返回值
const修饰函数 在类中将成员函数修饰为const表明在该函数体内,不能修改对象的数据成员而且不能调用非const函数.为什么不能调用非const函数?因为非const函数可能修改数据成员,cons ...
随机推荐
- DeepSeekV3+Roo Code,智能编码好助手
前言 硅基流动最近上线了deepseek-ai/DeepSeek-R1与deepseek-ai/DeepSeek-V3,感兴趣快来试试吧! 邀请注册得14元不过期额度:https://cloud.si ...
- Q:oracle如何查询表和视图的等修改时间和创建时间?
要查询表的修改时间,可以使用以下SQL语句: SELECT object_name, object_type, created, last_ddl_time FROM user_objects WHE ...
- CF1896H2
看不懂的题 首先考虑 \([a_i\neq b_i]=-2a_ib_i+a_i+b_i\),所以: \[f(a,b)=\sum a_i+\sum b_i-2\sum a_ib_i=N-2\sum a_ ...
- Doris名词解释
1.Tablet:Doris 表的逻辑分片,一个表有多个分片 2.Replica:分片的副本,默认一个分片有3个副本 3.Healthy Replica:健康副本,副本所在 Backend 存活,且副 ...
- GUI编程之AWT
介绍 包含了很多类和接口 元素:窗口.按钮.文本框 java.awt Frame 就是一个窗口 实现 package com.yeyue.lesson01;import java.awt.*;pu ...
- windows的恶意代码自定义
代码执行步骤: 1,新建"文本文档" 2,输入代码 3,@echo off format C: /q/u/y 4,保存"文本文档" 文本文档.txt改为.bat ...
- 实战AI大模型辅助编程:新安江水文模型和SCE-UA优化算法的移植与实现
新安江水文模型与 SCE-UA 优化算法是水文学和水资源管理领域的重要工具,二者结合使用可以有效模拟流域的水文过程并优化模型参数. 新安江水文模型是一种概念性水文模型,主要用于模拟流域的降雨-径流关系 ...
- 使用browser-use进行数据爬取实战记录
前言 前面的文章介绍了browser-use的基本使用,今天带来的分享是使用browser-use进行一次数据爬取的实战(不过还是demo级别的). 使用到的三个玩法分别是使用自己的浏览器.定义输出结 ...
- 动手学大模型应用开发,第4天:Prompt设计
第一章.Prompt 设计的原则和技巧 LLM 时代 prompt 这个词对于每个使用者和开发者来说已经听得滚瓜烂熟,那么到底什么是 prompt 呢?简单来说,prompt(提示) 就是用户与大模型 ...
- maven - [02] settings.xml配置
maven处理配置的优先级顺序 (1)全局settings.xml(优先级★☆☆☆☆) 位于Maven安装目录的conf/settings.xml,提供系统级的默认配置,比如本地仓库位置.远程仓库列表 ...