【Go函数详解】二、参数传递、变长参数与多返回值

news/2024/11/9 16:58:23/

文章目录

  • 一、传递参数
    • 1. 按值传参
    • 2. 引用传参
      • 2.1 特殊情况
        • 2.1.1 切片slice
        • 2.1.2 字典map
  • 二、变长参数
    • 1. 基本定义和传值
      • 1.1 基本定义
      • 1.2 传值
        • 1.2.1 普通传值
        • 1.2.2 传递切片
    • 2. 任意类型的变长参数(泛型)
  • 三、多返回值
    • 1. 命名返回值


一、传递参数

1. 按值传参

Go语言默认使用按值传参来传递参数,即传递参数值的一个副本:
函数接收到传递进来的参数后,会将参数值拷贝给声明该参数的变量(形式参数,简称形参),如果在函数体中有对参数值做修改,实际上修改的是形参值,这不会影响到实际传递进来的参数值(简称实参)。

go">func add(a, b int) int  {a *= 2b *= 3return a + b
}func main()  {x, y := 1, 2z := add(x, y)fmt.Printf("add(%d, %d) = %d\n", x, y, z)   //add(1, 2) = 8
}

x、y把值传递给a、b,主要原因是x、y的内存地址与a、b的内存地址并不相同,可以看作是两个值相等的不同变量,所以修改a、b的值并不会改变x、y的值。

2. 引用传参

如果想要实现在函数中修改形参值可以同时实参值,需要通过引用传参来完成,此时传递给函数的参数是指一个指针,而指针代表的是实参的内存地址,修改指针引用的值即修改内存地址中存储的值,所以实参的值也会被修改。

! 注意:这种情况下,传递的变量是地址值的拷贝,所以从本质上讲还是按值传递。

go">func add(a, b *int) int {*a *= 2*b *= 3return *a + *b
}func main()  {x, y := 1, 2z := add(&x, &y)fmt.Printf("add(%d, %d) = %d\n", x, y, z)
}

在函数调用时,像切片(slice)、字典(map)、接口(interface)、通道(channel)这样的引用类型默认使用引用传参。

2.1 特殊情况

以下具体分析详见:(留个坑)

2.1.1 切片slice

把slice作为函数参数传递,若在函数内slice发生扩容的话,会让函数内外原本指向同一个底层数组的两个slice变量,分别指向两个不同的底层数组

2.1.2 字典map

把map作为函数参数传递,若在函数内map发生扩容的话,函数内外的map变量指向的底层内存仍是一致的。

二、变长参数

所谓变长参数指的是函数参数的数量不确定,即可以传递任意数量的参数到指定函数。合适地使用变长参数,可以让代码更简洁,尤其是输入输出类函数,fmt.Println的参数就是典型的变长参数。

1. 基本定义和传值

1.1 基本定义

在参数类型前加上...前缀,就可以将该参数声明为变长参数

go">func myfunc(numbers ...int) {for _, number := range numbers {fmt.Println(number)}
}

1.2 传值

1.2.1 普通传值
go">myfunc(1, 2, 3, 4, 5) 

函数 myfunc() 接受任意数量的参数,这些参数的类型全部是 int

1.2.2 传递切片

传递切片时需要末尾加上...作为标识,表示对应的参数类型是变长参数:

go">slice := []int{1, 2, 3, 4, 5}
myfunc(slice...)
myfunc(slice[1:3]...)

注:形如...type格式的类型只能作为函数的参数类型存在,并且是函数的最后一个参数

之所以支持传入切片,是因为从底层实现原理上看,类型...type本质上是一个切片,也就是[]type,这也是为什么上面的numbers可以用for循环来获取每个传入的参数值。

2. 任意类型的变长参数(泛型)

即指定变长参数类型为interface{}

go">func myPrintf(args ...interface{}) {for _, arg := range args {switch reflect.TypeOf(arg).Kind() {case reflect.Int:fmt.Println(arg, "is an int value.")case reflect.String:fmt.Printf("\"%s\" is a string value.\n", arg)case reflect.Array:fmt.Println(arg, "is an array type.")default:fmt.Println(arg, "is an unknown type.")}}
}func main() {myPrintf(1, "1", [1]int{1}, true)
}

在这里插入图片描述
这里实现的是泛型功能,Go语言并没有在语法层面提供对泛型的支持,目前只能自己通过反射和interface{}类型实现。
interface{}是一个空接口,可以用于表示任意类型。但这个范围过于宽泛,像C语言中的void一样,我们根本不知道真正传递进来的参数到底是什么类型的,这在强类型的静态语言中是不能接受的,为了保证代码类型安全,需要在运行时通过反射对数据类型进行检查,以便让程序在预设的轨道内运行,避免因为类型问题导致程序崩溃。

三、多返回值

Go语言与其他编程语言一大不同之处在于支持多返回值,这才处理程序出错的时候非常有用。

go">func add(a, b *int) (int, error) {if (*a == 0 || *b == 0) {err := errors.New("只支持非负整数相加")return 0, err}*a *= 2*b *= 3return *a + *b, nil
}func main()  {x, y := -1, 2z, err := add(x, y)if err != nil {fmt.Println(err.Error())return}fmt.Printf("add(%d, %d) = %d\n", x, y, z)
}

如上,通过error指定多返回一个表示错误信息的、类型为error的返回值,函数的多个返回值之间可以通过逗号分隔,并且在最外面通过圆括号包起来。

1. 命名返回值

在设置多返回值时,可以对返回值进行变量命名,这样可以在函数中直接对返回值进行赋值,而不必每次都按照指定的返回值格式返回多个变量了。

go">func add(a, b *int) (c int, err error) {if (*a == 0 || *b == 0) {err = errors.New("只支持非负整数相加")return}*a *= 2*b *= 3c = *a + *breturn
}

这种机制避免了每次进行 return 操作时都要关注函数需要返回哪些返回值,为开发者节省了精力,尤其是在复杂的函数中。


http://www.ppmy.cn/news/1517707.html

相关文章

中标麒麟v10 sp3 部署cuda cudnn tensorrt deepstream

1.驱动安装 ./NVIDIA-Linux-x86_64-530.41.03.run 更改,不检测 ./NVIDIA-Linux-x86_64-530.41.03.run -no-x-check 禁用nouveau 创建文件/etc/modprobe.d/blacklist-nouveau.conf,添加如下文本: blacklist nouveau options nouveau modeset=0 重新生成initramfs $ su…

使用Python 访问Neo4J 数据库

前期准备工作 安装Neo4j Python库 要与Neo4j数据库交互,你需要安装Neo4j的Python客户端库。可以通过以下命令安装: pip install neo4j基本步骤 以下是使用Neo4j的基本步骤,每一步都包含了详细的说明: #mermaid-svg-pxB8pSi7m2…

DWF 支持的 TON 链 Telegram 免费宠物游戏 Gatto_game,推出 “Paws Up! 世界锦标赛”

TON 链在这轮牛市里无疑是一匹脱缰的黑马,创造了一个又一个爆款,为持有者带来了不菲的收益。 Gatto_game 是一款 TON链 Tamagotchi 电子宠物风格的 P2E web3 游戏。可以通过喂养升级,参加比赛赚取 $TON 或者 $GTON ,或许就是下一个…

秋招突击——算法练习——8/26——图论——200-岛屿数量、994-腐烂的橘子、207-课程表、208-实现Trie

文章目录 引言正文200-岛屿数量个人实现 994、腐烂的橘子个人实现参考实现 207、课程表个人实现参考实现 208、实现Trie前缀树个人实现参考实现 总结 引言 正文 200-岛屿数量 题目链接 个人实现 我靠,这道题居然是腾讯一面的类似题,那道题是计算最…

从零开始掌握容器技术:Docker的奇妙世界

容器技术在当今的云计算和软件开发领域中扮演着越来越重要的角色。如果你是一名计算机专业的学生或从事IT行业的从业者,可能已经听说过Docker这个词。它在软件开发、部署、运维等环节中大放异彩,但对于刚接触这个概念的朋友来说,可能还是有些…

sql主从表的区分

主键约束比较好理解,就是主键值不能为空且不重复,已经强调好多次,所以这里重点记录对外键约束的学习。 主表与从表 若同一个数据库中,B表的外键与A表的主键相对应,则A表为主表,B表为从表。 就是两个表有相…

在Vue2中使用WebSocket

WebSocket是一种通信协议,能够在客户端和服务器之间建立全双工通信通道。与传统的HTTP请求不同,WebSocket允许服务器主动向客户端发送数据,从而实现实时通信。本文将详细介绍如何在Vue2项目中集成和使用WebSocket,并通过具体代码示…

在离线环境中安装依赖包

在离线环境中安装依赖包的方法: 参考文章: 1.如何离线下载python依赖包 2.python 依赖包打包 离线下载 3.Python的国内安装源(也称为镜像源) 最终安装效果,有部分失败的,重新下载失败的再去试试