Go——函数

一. 函数定义

        1.1 特点

  • 无需声明原型
  • 支持不定变参
  • 支持多返回值
  • 支持命名返回参数
  • 支持匿名函数和闭包
  • 函数也是一种类型,一种函数可以赋值给变量
  • 不支持嵌套,一个包不能有两个名字一样的函数
  • 不支持重载
  • 不支持默认参数

        1.2 函数声明

        函数声明包含一个函数名,参数列表,返回值列表和函数体。如果没有返回值,则返回值列表可以省略。函数从第一条语句开始执行,直到执行return语句或者函数体的最后一条语句。

        函数可以没有参数或者接受多个参数。注意:类型在变量名后。

        当两个或者两个以上的函数命名参数是同一类型,则除最后一个类型之外,其它可以省略。

        函数可以返回任意数量的返回值。

        使用关键字func定义函数,左括号不能另起一行。

func test(s string, x, y int) (int, string) {n := x + yreturn n, fmt.Sprintf(s, n)
}

        函数是第一类对象,可作为参数传递。建议将复杂签名定义为函数类型,以便于阅读。 

        有返回值的函数,必须有明确的终止语句,否则会引发编译错误。

        你可能会偶尔遇到没有函数体的函数声明,这表示该函数不是以Go实现的。这样的声明定义了函数标识符。

package mathfunc Sin(x float64) float //用汇编语言实现

        1.3 参数

        函数定义时指出,函数定义时有参数,该变量可以称为函数形参。形参就像定义在函数体内的局部变量。

        但调用函数时,传递过来的变量就是函数的实参。函数可以通过两种方式来传递参数:

        值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响实际参数。

        引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数进行修改,将影响实际参数。

        在默认情况下,Go语言是值传递,即在调用过程中,不会影响实际参数。

        注意:

  • 无论是值传递还是引用传递,传递给函数的都是变量的副本。不过,值传递是值的拷贝,引用传递是指针的拷贝(解引用后就是实际的值)。
  • 一般来说,地址拷贝更加高效(指针在32位下占4字节,在64位下占8字节)。而值拷贝取决于拷贝对象的大小,对象越大性能越低。
  • map,slice,chan,指针,interface默认以引用传递

不定参数传递:

        不定参数传值,就是函数的参数不是固定的,后面的类型是固定的。(可变参数)

        Golang可变参数的本质上就是一个切片slice。只能有一个,且必须是最后一个。

        在参数赋值时,可以不用一个个的赋值,可以直接传递一个切片。注意传递切片时在可变参数后需要加"..."。

func myFunc1(args ...int) {//0个或多个参数}func myFunc2(a int, args ...int){//1个或多个参数}func myFunc3(a int, b int, args ...int){ //2个或多个参数}

注意:其中args是一个slice,我们可以通过arg[index]依次访问所有的参数,通过len(args)来判断参数的个数。

任意类型的不定参数: 

         任意类型的不定参数,就是函数参数个数和每个参数的类型都是不固定的。

        用户interface{}传递任意类型数据是Go语言的常用惯例,而且interface{}是类型安全的。

func myFunc(args ...interface{}) {}

        1.4 返回值 

特点

  • "_"标识符,用来忽略函数的返回值
  • Go的返回值可以被命名,并且就像函数开头声明的变量那样使用
  • 返回值的名称应当具有一定的意义,可以作为文档使用
  • 没有参数的return语句返回各个返回变量的当前值(用在返回值有名称的情况)。这种用法被称作"裸"返回
  • 直接返回语句仅应当用在短函数中。在长函数中它们会影响代码的可读性。
  • 命名返回值参数可看做与形参类似的局部变量,最后由return隐式返回 

  • Golang返回值不能用容器对象接收多返回值。只能用多个变量,或者"_"忽略返回值。 即有几个返回值,就需要几个变量接收

  • 多返回值可以直接作为其它函数调用的实参 

 

  • 命名返回参数可被同名局部变量遮掩,此时需要显示返回

  • 命名返回参数允许defer延迟调用通过闭包读取和修改 

  • 显示return返回前,会先修改命名返回参数 

        1.5 匿名函数

        匿名函数是指不需要定义函数名的一种函数实现方式。 

        在Go语言中,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。

        匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优点在于可以直接使用函数内的变量,不必声明。

         对比C/C++,函数虽然也可以作为变量或参数,但是函数需要有名字。

        Golang匿名函数可以赋值给变量,作为结构体字段,或者在channel里传送。

        1.6 闭包

        闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

        官方的解释是:所谓闭包,指的是一个拥有许多变量和绑定了这些变量的环境表达式,通常是一个函数,因而这些变量也是表达式的一部分。

        维基百科讲,闭包是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,由另外一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有很多实例,不同的引用环境和相同的函数组合可以参数不同的实例。

        看着上面的描述,会发现闭包和匿名函数很像。下面来一个例子。

  • 定义 

         函数b嵌套在函数a内部,函数a返回函数b,这样执行完var c=a()后,实际c指向了函数b(),再执行c()会执行函数b打印变量i,第一次为1,第二次为2,第三次为3,以此类推。其实,这段代码就创建了一个闭包。因为函数a()外的变量c引用了函数a()内的函数b()。也就是说:当函数a()的内部函数b()被函数a()外的一个变量引用的时候,就创建了一个闭包。

        在上面例子中,由于闭包的存在使得函数a返回后,a中的变量i始终存在,这样每次执行c(),i都是自加1后的值。闭包使得Go的垃圾回收机制GC不会回收a()占用的资源。

  • 作用

        在给定函数被多次调用的过程中,这些私有变量能够保持其持久性但是变量的作用域仅限于包含它们的函数,所以在其它程序代码中无法进行访问。变量的生存周期变长,在一次函数调用期间所创建生成的值在下次函数调用时仍然存在(所以上面打印的值为1,2,3)。正因为上面的特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中。

        对于上面的例子,如果函数a返回的不是函数b,情况就完全不同了。因为函数a执行完后,函数b没有被返回给函数a的外界,只是被函数a引用了,函数a被函数b引用。所以函数a和函数b互相引用不被外界引用。函数a和b会被垃圾回收机制GC回收。

  • 引用环境

        上面的例子,c()跟c2()引用的是不同的环境,在调用i++时修改的不是同一个i,因此开始时输出的都是1。函数a()每进入一次,就形成了新的环境,对应的闭包中,函数都是同一个函数,环境确是引用不同的环境。这和c()和c1()的调用顺序无关。

        闭包进入一次,引用环境不同。

  • 闭包复制的是原对象指针,这就很容易解释延时引用现象

        匿名函数变量i和test()函数变量i是同一个。

        在汇编层,test实际返回的是FuncVal对象,其中包含匿名函数地址,闭包对象指针。当调用匿名函数时,只需以某个寄存器传递该对象即可。

FuncVal { func_address, closure_var_pointer ... }
  •  外部引用函数参数的局部变量

  • 返回两个闭包

         1.7 递归

        递归,就是在运行的过程中调用自己。一个函数调用自己,就叫做递归函数。

        构成递归需具备的条件:

  1. 子问题须与原始问题为同样的事情,且更为简单
  2. 不能无限制地调用本身,必须有个出口,化简为非递归状况处理
  • 数字阶乘

        一个正整数的阶乘是所有小于等于该数地正整数地积,并且0的阶乘为1。

package mainimport "fmt"func factorial(x int) int {if x <= 1 {return 1}return x * factorial(x-1) //自己调用自己
}func main() {res := factorial(5)fmt.Println(res)
}
  • 斐波那契数列

        1.8 延时调用(defer)

        defer特性:

  • 关键字defer用于注册延时调用
  • 这些调用直到return前才被执行。因此,可以用来做资源清理
  • 多个defer语句,按先进后出的方式执行。因为后面的语句会依赖前面的资源,因此如果前面的资源释放了,后面的语句就没法执行了。
  • defer语句中的变量,在defer声明前就决定了。

        defer用途:

  • 关闭文件句柄
  • 锁资源释放
  • 数据库连接释放

        defer触发时机:

  • 包裹着defer语句的函数返回时
  • 包裹着defer语句的函数执行完时
  • 当前goroutine发送Panic时

        实例:

        defer先进后出:

  • 当go遇到一个defer语句时,不会立即执行,而是将defer后面的语句压入到一个栈中,然后继续执行函数下的语句。
  • 当函数或方法执行完毕,再从栈中依次从栈顶取出执行(先入后出)。

         i是从0到4,但是由于defer先进后出,所以是4到0。

        defer遇上闭包:

  • 在Go语言中,对外部作用域中变量访问的方式是"引用",捕获的是变量的地址。当外部变量发生改变时,闭包中的变量也会发生改变。

  • 多个defer语句注册,按先入后出次序执行。哪怕函数或某个延时调用发生错误,这些调用依旧会被执行。 

  •  延迟调用参数在注册时求值或复制,可以用指针或闭包"延时读取"

  • 滥用defer可能导致性能问题,尤其是在一个大循环里面 

defer陷阱: 

  • defer与闭包:

        由于闭包使用的外部变量保存的是地址。修改外部变量可以得到最新的值。

  • defer与return

  • defer nil函数 

        值得注意的是run在声明是不会报错,而是在使用是报错。

  •  在错误位置使用defer

        当http.Get失败时会抛异常。

        修改:

        获取返回错误:

  • 释放相同资源 

         解决方案:不建立闭包,将变量传进来,这样是值拷贝。

         1.9 异常处理

        Golang没有结构化异常,使用panic抛出错误,recover捕获错误。

        使用场景:Go中可以抛出一个panic异常,然后在defer中通过recover捕获这个异常,然后正常处理。

        panic:

  • 内置函数
  • 假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
  • 返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行
  • 直到goroutine整个退出,并报告错误

        recover:

  • 内置函数
  • 用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为。 
  • 一般的调用建议:
    • 在defer函数中,通过recever来终止一个goroutine的panicking过程,从而恢复正常代码的执行。
    • 可以通过panic传递错误

        注意:

  • 利用recover处理panic指令,defer必须放在panic之前定义,另外recover只有在defer调用函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
  • recover处理异常后,逻辑并不会恢复到panic那个点去,函数跑到defer之后的那个点
  • 多个defer会形成defer栈,后定义的defer语句会被最先调用

 由于panic和recover参数类型为interface{},因此可以抛出任何类型的对象

func panic(v interface{})
func recover()interface{}
  •  向已关闭的通道发送数据会引发panic

  • 延时调用中引发错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获

 

  • 当异常panic被回收recover后,就没了,后面的recover回收不到该异常

  • 捕获函数recover只有在延时调用内直接调用才会终止错误,否则捕获不到异常(返回nil),任何未捕获的的错误都会沿调用堆栈向外传递。

         也可以不使用匿名函数,而是使用函数,也可以回收异常。

  • 如果需要保护代码段,可将代码重构成匿名函数,如此可确保后续代码被执行

  • 除了用panic来引发中断性错误外,还可返回error类型错误对象来表示函数调用状态
type error interface{Error() string
}

        标志库errors.New和fmt.Errorf函数用于创建实现error接口的错误对象。通过判断错误对象实例来确定具体错误类型。

        panic用来返回error接口对象:

  • Go实现类似try catch的异常处理

如何区分使用panic和error两种方式?

        导致关键流程出现不可修复性错误的使用panic,其它使用error(返回error)。 

 

 

 

 

 

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

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

相关文章

备战蓝桥杯---DP刷题2

1.树形DP&#xff1a; 即问那几个点在树的直径上&#xff0c;类似ROAD那题&#xff0c;我们先求一下每一个子树根的子树的最大值与次大值用d1,d2表示&#xff0c;直径就是d1d2的最大值&#xff0c;那么我们如何判断是否在最大路径上&#xff0c;其实就是看一下从某一点出发的所…

还得是抖音,字节推出竖屏视频理解数据集,入选CVPR2024

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 短视频在当下社交媒体逐渐成为主导的视频格式。传统视频处理技术和研究一般都专注于横屏视频…

58商铺全新UI试客试用平台网站php源码

探索未来商铺新纪元&#xff0c;58商铺全新UI试客试用平台网站PHP源码完整版震撼来袭&#xff01; 在这个数字化飞速发展的时代&#xff0c;58商铺一直致力于为商家和消费者打造更加便捷、高效的交易平台。今天&#xff0c;我们荣幸地推出全新UI试客试用平台网站PHP源码完整版…

C/C++ 项目:分别用精密星历和广播星历计算卫星坐标

文章目录 Part.I IntroductionChap.I rinex.hChap.II gmain_body.h Part.II 使用方法扩展阅读 Part.I Introduction 本文将介绍一个小项目的使用方法&#xff0c;此项目可用精密星历和广播星历计算卫星位置&#xff0c;并将两者结果做差&#xff0c;输出至文件。 其实 『分别…

SWM341系列应用(上位机应用)

SWM341系列之上位机应用 1、分级图像和PNG、JPG的应用 现象&#xff1a;客户使用SWM34SVET6HMI_0.4.1版本上位机进行UI界面布局&#xff0c;反馈在模拟运行时&#xff08;PC端&#xff09;流畅&#xff0c;在Demo平台&#xff08;设备端&#xff09;运行卡顿。 分析及解决&…

【fastadmin】脚本模式下,日志钩子函数执行出现死循环,导致内存溢出奔溃

问题出现原因是想对项目中error级别的日志&#xff0c;接入钉钉告警&#xff0c;方便查看 于是使用钩子方法&#xff0c;日志写入完成后&#xff0c;自动调用自定义的告警方法中 1、在application/tags.php 中添加log_write_done > [app\\common\\behavior\\Common, ],2、在…

【THM】Nmap Post Port Scans(后端口扫描)-初级渗透测试

介绍 本房间是 Nmap 系列的最后一个(网络安全简介模块的一部分)。在这个房间中,我们重点关注端口扫描之后的步骤:特别是服务检测、操作系统检测、Nmap脚本引擎和保存扫描结果。 Nmap实时主机发现Nmap基本端口扫描Nmap高级端口扫描Nmap后端口扫描在本系列的第一个房间中,我…

代码随想录第29天|491.递增子序列 46.全排列 47.全排列 II

目录&#xff1a; 491.递增子序列 46.全排列 47.全排列 II 491.递增子序列 491. 非递减子序列 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 回溯算法精讲&#xff0c;树层去重与树枝去重 | LeetCode&#xff1a;491.递增子序列_哔哩哔哩_bili…

DTFT及其反变换的直观理解

对于离散时间傅里叶变换(DTFT)及其反变换的讲解&#xff0c;教材里通常会先给出DTFT正变换的公式&#xff0c;再举个DTFT的简单变换例子&#xff0c;推导一下DTFT的性质&#xff0c;然后给出DTFT反变换的公式&#xff0c;再证明一下正变换和反变化的对应关系。总的来说就是&…

Spring-IoC 基于xml管理

现大多使用注解方式&#xff0c;xml方式并不简洁&#xff0c;本文仅记录xml用作基础学习。 0、前提 首先在父项目的pom.xml中配置好依赖们。然后子模块也可以使用这些依赖。 在resource目录下创建Spring的xml文件&#xff0c;名称无要求&#xff0c;本文使用bean.xml。文件最…

黄锈水过滤器 卫生热水工业循环水色度水处理器厂家工作原理动画

​ 1&#xff1a;黄锈水处理器介绍 黄锈水处理器是一种专门用于处理“黄锈水”的设备&#xff0c;它采用机电一体化设计&#xff0c;安装方便&#xff0c;操作简单&#xff0c;且运行费用极低。这种处理器主要由数码射频发生器、射频换能器、活性过滤体三部分组成&#xff0c;…

uniapp uni.scss中使用@mixin混入,在文件引入@include 样式不生效 Error: Undefined mixin.(踩坑记录一)

问题&#xff1a; 在uni.scss文件定义mixin 2. 在vue文件引入: 3. 出现报错信息: 4. 问题思考&#xff1a; 是不是需要引入uni.scss &#xff1f; 答案不需要 uni.scss是一个特殊文件&#xff0c;在代码中无需 import 这个文件即可在scss代码中使用这里的样式变量。uni-app的…

原创【matcap材质在ue4中的实现办法】

matcap材质在ue4中的实现办法 2023-08-29 15:34 https://www.bilibili.com/video/BV1GR4y1b76n/?spm_id_from333.337.search-card.all.click&vd_sourced76b773892c830a157c0ccc97ba78411 评论(0)

《C Prime Plus》02

1. UNIX 系统 C语言因UNIX系统而生&#xff0c;也因此而流行&#xff0c;所以我们从UNIX系统开始&#xff08;注意&#xff1a;我们提到的UNIX还包含其他系统&#xff0c;如FreeBSD&#xff0c;它是UNIX的一个分支&#xff0c;但是由于法律原因不使用该名称&#xff09;。 UN…

【运输层】网络数据报协议 UDP

目录 1、UDP 的特点 2、UDP 的首部格式 UDP 只在 IP 协议之上增加了很少的一些功能&#xff0c;比如复用、分用以及差错检测等。 1、UDP 的特点 UDP是无连接的&#xff0c;即发送数据之前不需要建立连接&#xff0c;因此减少了开销和发送数据之前的时延。 UDP使用尽最大努力…

基于vscode Arduino插件开发Arduino项目

基于vscode Arduino插件开发arduino项目 插件配置问题记录1. 指定编译输出文件夹2. 编译下载时不输出详细信息3. 输出端口信息乱码4. 通过串口输出中文&#xff0c;vscode对应的串口助手上会显示乱码&#xff08;未解决&#xff09; 插件配置 环境&#xff1a;Arduino插件版本…

苏州金龙助力旅游客运加速蜕变

近日&#xff0c;北京铭悦旅游客运有限公司又迎来一批苏州金龙海格纯电动客车。&#xff08;以下简称北京铭悦旅游&#xff09;总经理郭保生在车辆交付时说到&#xff0c;“为迎接强劲复苏的旅游市场&#xff0c;要求旅游客运向绿色客运转型&#xff0c;以及人民对品质生活、美…

【LeetCode热题100】51. N 皇后(回溯)

一.题目要求 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方…

4.3学习总结

[HNCTF 2022 WEEK2]Canyource&#xff08;无参数&#xff09; 通过这题又接触了一种无参数RCE的方法&#xff0c;前面学习的getallheaders只有在apache环境下才能使用&#xff0c;具有一定的局限性 这里是利用php函数来构造读取flag的方法 localeconv() – 函数返回一个包含本…

当Pycharm中右键运行python程序时出现Run ‘pytest in tests ***py‘,如何解决?

1、在Pycharm中右键运行python程序时出现Run pytest in tests ***py &#xff0c;这是进入了Pytest模式。 2、解决办法 进入到File->Settings->Tools->Python integrated Tools页面或者快捷键&#xff08;CtrlAltS&#xff09; 找到Testing下的Default test runner …