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

news2024/9/28 9:29:59

文章目录

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

func add(a, b int) int  {
    a *= 2
    b *= 3
    return a + b
}

func main()  {
    x, y := 1, 2
    z := 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. 引用传参

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

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

func add(a, b *int) int {
    *a *= 2
    *b *= 3
    return *a + *b
}

func main()  {
    x, y := 1, 2
    z := 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 基本定义

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

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

1.2 传值

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

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

1.2.2 传递切片

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

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

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

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

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

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

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

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

func main()  {
    x, y := -1, 2
    z, 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. 命名返回值

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

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

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

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

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

相关文章

破解电商数据分析难题,优化运营策略的秘诀

在电商行业中,数据分析是不可或缺的一部分。它能帮助商家精准掌握市场动态,优化运营策略,从而提升销售业绩。然而,面对大量复杂的数据,许多电商运营者往往不知道从哪里开始分析。那么,电商运营究竟如何有效…

优可测白光干涉仪助力红外探测行业发展——晶圆衬底检测

从18世纪红外线被发现,到19世纪红外探测器的发明。至今,随着工艺更新迭代,红外探测器朝着多波段、大面阵、高分辨率、低成本量产快速发展。 今天,小优博士带您探索红外探测的奥秘。 一、红外线是什么 红外光是一种电磁波&#x…

【 OpenHarmony 系统应用源码解析 】-- Launcher 初体验

前言 最近因为业务需要,需要做一款 UI 定制的鸿蒙 Launcher,于是就开始了「找到代码」、「研究代码」、「魔改代码」的套路流程,仅以此文章作为知识备份和技术探讨所用,也希望能给其他小伙伴提供一些源码的解析思路,方…

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——8.stackqueuepriority_queue(模拟实现)

1.stack 可通过模板使用其他类来建立stack&#xff08;如vector&#xff0c;list&#xff09; #include<vector>namespace zone {template<class T,class container> //两个模板参数class stack{public:void push(const T& x){it.push_back(x); //使用it的p…

【Linux】命令简介------迅速掌握Linux命令

目录 Linux 命令 &#x1f354; ls命令 &#x1f354; cd 和 pwd命令 &#x1f354; 相对路径和绝对路径 &#x1f354; 文件/文件夹的创建以及文件内容的浏览 &#x1f354; 文件的复制,移动和删除 &#x1f354; 文件的查找 &#x1f354; grep 和管道 &#x1f354…

Windows11安装SqlLite、Navicat Premium 15连接SqlLite、Springboot集成SqlLite

一、Windows11安装SqlLite 1、下载安装包 地址&#xff1a;SQLite Download Page 2、压缩包解压 3、配置系统环境变量 4、验证安装是否成功 打开命令提示符&#xff0c;输入 sqlite3 5、创建数据库文件 新建文件重命名为你想要的数据库名称&#xff0c;文件后缀改为.db 二、…

【微信小程序】如何触发按钮事件,例如调起微信客服

需求 实现一个如下图的效果, 点击客服按钮, 调起微信客服功能, 需要和button组合使用 效果图 实现思路 客服只能通过button按钮调起, 所以我们需要写一个button按钮, open-type“contact”, 然后把它隐藏起来。给客服图标加一个label, 设置for“btnId”, 这样点击图片就会触…

微服务即时通讯系统环境搭建(客户端)

微服务即时通讯系统环境搭建(客户端) 前言 今天开始&#xff0c;我们要开一个新坑&#xff0c;我们将它称作微服务即时通讯系统。说到即时通讯系统&#xff0c;大家肯定能想到如同“微信”这样的app。那么没错&#xff0c;这次这个项目就会像微信一样&#xff0c;当然功能肯定…

Linux(CentOS8)系统安装mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz

一、下载获取 mysql安装包&#xff1b; MySQL :: Download MySQL Community Server (Archived Versions) 二、安装步骤 1、切换到安装目录下&#xff0c;并解压 tar -zxvf mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz 2.移动解压后的文件并且重命名为mysql mv mysql-8.0.26…

Mybatis:基础巩固-DCL

目录 一、概述二、用户管理2.1 查询用户2.2 创建用户2.3 修改用户密码2.4 删除用户 三、权限控制3.1 查询权限3.2 赋予权限3.3 撤销权限 一、概述 DCL数据控制语言&#xff0c;用来管理数据库用户、控制数据库的访问和权限。简单来说就是可以让哪些用户可以访问哪些数据库。 二…

LiveQing视频点播流媒体RTMP推流服务功能-支持OBS推流摄像机RTMP推流支持无人机RTMP推流解决大疆无人机推流花屏问题完美解决大疆无人机花屏

LiveQing-支持OBS推流摄像机RTMP推流支持无人机RTMP推流解决大疆无人机推流花屏问题完美解决大疆无人机花屏 1、流媒体服务搭建2、推流工具准备3、创建鉴权直播间4、获取推流地址5、配置OBS推流6、推流及播放7、获取播放地址7.1 页面查看视频源地址7.2 接口查询 8、更多问题8.1…

黑屏环境下,如何利用OBD部署OceanBase企业版集群

一、前言 OBD&#xff0c;作为OceanBase官方推出的部署工具&#xff0c;显著简化了OB单机及集群的部署流程。此前&#xff0c;OBD能够支持对社区版OB进行一键部署&#xff0c;那OBD是否同样支持OB企业版的部署呢&#xff1f; 本文为大家介绍通过OBD&#xff0c;在OB企业版集群…

短视频矩阵系统怎么开发搭建使用?解决内容创作分发效率问题的工具系统

目录 前言 &#xff1a; 一、短视频矩阵系统开发目的 系统主要功能 二、怎么开发 前言 &#xff1a; 短视频矩阵系统是一种综合性的短视频营销工具&#xff0c;它集成了短视频创作、管理、分发、数据分析等多种功能于一体。 一、短视频矩阵系统开发目的 在帮助创作者和企…

C++中类的相关学习

动态内存分配和回收&#xff08;堆区&#xff09; C语言中动态内存分配和回收使用malloc函数和free函数完成的 C依旧可以使用上述的两个函数完成&#xff0c;动态内存分配和回收 C也可以使用两个关键字new和delete来完成动态内存的分配和回收 内存分配 单个申请 格式&…

Chapter 07 watch侦听器

欢迎大家订阅【Vue2Vue3】入门到实践 专栏&#xff0c;开启你的 Vue 学习之旅&#xff01; 文章目录 前言一、基本用法二、深度侦听 前言 在 Vue 中&#xff0c;watch 侦听器是一个非常实用的工具&#xff0c;用于处理自定义数据的变化。本文详细讲解了 watch 侦听器的基本用法…

Pytorch实现多层LSTM模型,并增加emdedding、Dropout、权重共享等优化

简述 本文是 Pytorch封装简单RNN模型&#xff0c;进行中文训练及文本预测 一文的延申&#xff0c;主要做以下改动&#xff1a; 1.将nn.RNN替换为nn.LSTM&#xff0c;并设置多层LSTM&#xff1a; 既然使用pytorch了&#xff0c;自然不需要手动实现多层&#xff0c;注意nn.RNN…

JVM1-初识JVM

目录 什么是JVM JVM的功能 解释和运行 内存管理 即时编译 Java性能低的主要原因和跨平台特性 常见的JVM 什么是JVM JVM 全称是 Java Virtual Machine&#xff0c;中文译名&#xff1a;Java虚拟机 JVM本质上是一个运行在计算机上的程序&#xff0c;它的职责是运行Java字…

AI大模型编写多线程并发框架(六十三):监听器优化·下

系列文章目录 文章目录 系列文章目录前言一、项目背景二、第十一轮对话-修正运行时数据三、修正任务计数器四、第十二轮对话-生成单元测试五、验证通过七、参考文章 前言 在这个充满技术创新的时代&#xff0c;AI大模型正成为开发者们的新宠。它们可以帮助我们完成从简单的问答…

C++,如何写单元测试用例?

文章目录 1. 概述1.1 什么是单元测试&#xff1f;1.2 为什么要做单元测试&#xff1f; 2. 写测试用例的方法3. 编写测试用例的通用原则3.1 目的性原则3.2 独立性原则3.3 可重复性原则3.4 小规模原则3.5 一致性原则3.6 自动化原则3.7 边界条件原则3.8 错误检测原则3.9 性能原则3…

西门子PLC控制激光读头,profient转Ethernet IP网关应用

在智能制造的浪潮下&#xff0c;企业对于生产线的灵活性、智能化水平以及数据交互能力提出了更高要求。西门子PLC以其高可靠性和丰富的功能模块&#xff0c;广泛应用于各种自动化生产线中。而激光读头作为精密测量与定位的关键设备&#xff0c;其高精度、非接触式测量特性在自动…