Go语言之函数(定义、可变参数、匿名函数、error、panic、recover、计算函数运行时间)
创始人
2024-06-03 17:10:45
0

一、函数的定义

/*
函数的定义格式
func 函数名(形参列表)(返回值列表){
}
*/package mainimport ("fmt""math"
)//函数一:
func Add(a, b int)int{return a + b
}
//函数二:
func Sub(a, b int)int{return a - b
}
//函数三:
func hhh(a, b float64)float64{return math.Sqrt(a*a + b*b)
}
func main() {c := Add(10, 11)d := Sub(23, 30)e := hhh(40,30)fmt.Println("c=",c)fmt.Println("d=",d)fmt.Println("e=",e)
}

结果:
在这里插入图片描述

二、函数返回值和可变参数

  • Go语言支持多返回值。在Go中,经常使用多返回值中的最后一个返回值参数返回函数执行中可能发生的错误err

1.同一种类型的返回值

  • 若返回值是同一种类型,则用括号将多个返回值类型括起来,用逗号分隔每个返回值类型

    /*
    函数的定义格式
    func 函数名(形参列表)(返回值列表){
    }
    */package mainimport ("fmt"
    )func hhh()(int, int){a := 100var b int=300return a, b
    }
    func main() {c, d := hhh()fmt.Println("c=",c)fmt.Println("d=",d)
    }
    

    结果:
    在这里插入图片描述

2.带有变量名的返回值

  • Go语言支持对返回值进行命名,这样返回值就和参数一样有参数变量名和类型;命名的返回值变量的默认值为类型的默认值,即数值为0,字符串为空字符串,布尔值为false,指针为nil等。

    package mainimport ("fmt"
    )func hhh()(a bool, b int){a = trueb = 300return 
    }
    func main() {c, d := hhh()fmt.Println("c=",c)fmt.Println("d=",d)
    }
    

    结果:
    在这里插入图片描述

3.可变参数

  • 可变参数是指函数传入的参数个数是可变的。…interface{}
    package mainimport "fmt"//可变参数列表 ...interface{}
    func hhh(args ...interface{})(){for _,arg := range args{switch arg.(type){case int:fmt.Println(arg,"is type of int")case float64:fmt.Println(arg,"is type of float64")case bool:fmt.Println(arg, "is type of bool")case string:fmt.Println(arg,"is type of string")default:fmt.Println(arg,"is the unknown type")}}
    }
    func main() {var i int=100var f float64=3.1415var b bool=truevar s string="hello world"var iptr *int=&ihhh(i, f, b, s, iptr)
    }
    
    结果:在这里插入图片描述

三、函数变量

  • 在Go中,函数也是一种类型,可以和其他类型一样,保存在变量中。如下所示
    package mainimport ("fmt"
    )func hhh(a bool, b int)(){fmt.Println("a=",a)fmt.Println("b=",b)
    }
    func main() {var fnc func(bool, int)fnc = hhh fnc(true, 10000) //实际调用的是hhh函数
    }
    
    结果在这里插入图片描述

四、匿名函数

  • Go支持匿名函数,即在需要使用函数时再定义函数,匿名函数没有函数名只有函数题,函数可以作为一种类型被赋值给函数类型的变量,匿名函数也往往以变量的方式进行传递
  • 匿名函数是指不需要定义函数名的一种函数实现方式,由一个不带函数名函数声明和函数体组成。
    /*
    匿名函数语法:
    func(参数列表)(返回值列表){函数体
    }
    */package mainimport "fmt"func main() {//将匿名函数保存到变量res中res := func(a, b int){c := a + bfmt.Println("c = ", c)}//使用res()调用res(100,200)
    }
    
    结果:
    在这里插入图片描述

1.匿名函数作为参数传入

package mainimport "fmt"func visit(slc []int, f func(int)){for _,v := range slc{f(v)}
}
func main() {//使用匿名函数遍历打印切片,将匿名函数作为参数传入函数visit中visit([]int{1,2,3,4,5,6,7,8,9,10}, func(v int){fmt.Println(v)})
}

结果:
在这里插入图片描述

2.闭包Closure

  • 闭包:引用了外部变量的匿名函数。是指有权访问另一个函数作用域中的变量的函数。

  • Go中闭包是引用了自由变量的函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会被释放或者删除,在闭包中可继续使用这个自由变量,因此,闭包里作用域返回的局部变量不会被立刻销毁回收,但过度使用闭包可能会占用更多内存,导致性能下降。简单来说:
    闭包 = 函数 + 引用环境

  • 闭包的本质是函数,但是这个函数会用到函数外的变量,它们共同组成的整体即闭包。如下面的str和匿名函数共同组成的整体叫做闭包。

package mainimport "fmt"func main(){str := "hello world"fmt.Println("变成闭包前:")fmt.Println(str)//匿名函数中访问strfoo := func(){str = "hello sheena!!!!"}//调用匿名函数foo()fmt.Println("变成闭包后:")//闭包str拥有了记忆fmt.Println(str)
}

结果:
在这里插入图片描述

  • 闭包具体请看博客Go语言之闭包

五、函数类型实现接口

  • 把函数作为接口来调用
    package mainimport ("fmt"
    )//定义接口
    type Sheena interface {//接口中的方法MyPrint(interface{})
    }//定义接口体
    type MyStruct struct {}//结构体变量实现接口方法
    func (ms *MyStruct)MyPrint(pi interface{}){fmt.Println("from my struct" , pi)
    }
    //函数定义为类型
    type FuncCall func(interface{})//函数边领实现接口方法
    func (fc FuncCall)MyPrint(fi interface{}){//调用f函数本体fc(fi)
    }func main(){//声明接口变量var sea Sheena//实例化结构体st := new(MyStruct)//将实例化的结构体赋值到接口sea = st//使用接口调用实例化结构体方法Struct.MyPrintsea.MyPrint(33333)//将匿名函数转为FuncCall类型,再赋值给接口sea = FuncCall(func(v interface{}){fmt.Println("from function aaa", v)})//使用接口调用FuncCall,MyPrint,内部会调用函数本体即匿名函数sea.MyPrint(1111)
    }
    
    结果:
    在这里插入图片描述

六、defer延迟执行语句

  • Go中defer语句会将其后面跟着的语句进行延迟处理,在defer归属的函数即将返回时,将延迟处理语句按defer的逆序进行执行。即先被defer的语句最后执行,最后被defer的语句最先被执行。
  • 如下例所示,defer语句是在函数要退出的时候才开始执行,故先打印“defer begin”和“defer end”
    package mainimport "fmt"func main(){fmt.Println("::::defer begin::::")defer fmt.Println("a") //将defer放入延迟调用栈defer fmt.Println("b")defer fmt.Println("c")defer fmt.Println("d")defer fmt.Println("e") //最后一个放入,位于栈顶,最先调用fmt.Println("::::defer end::::")
    }
    
    结果:在这里插入图片描述

1.使用defer释放资源

  • 使用延迟执行语句在函数退出时释放资源,对于先加锁,再执行业务,最后解锁的代码
    package mainimport ("fmt""sync"
    )var vslueMutex sync.Mutex
    func hhhh(key string)int{mymp := map[string]int{"aaaa":111}vslueMutex.Lock()//defer后面的语句不会马上调用,而是延迟到函数结束时调用defer vslueMutex.Unlock()return mymp[key]
    }func main(){fmt.Println(hhhh("aaaa"))
    }
    

2.defer的原理

详情请看博客Go语言之defer(原理、常见的坑)

七、处理运行时错误

  • Go语言的错误:
    (1)一个可能造成错误的函数,需要返回值中返回一个错误接口(error),若调用是成功的,错误接口将返回nil,否则返回错误。
    (2)在函数调用后需要检查错误,若发生错误,则需要进行必要的错误处理。

1.错误接口的定义格式

  • error是Go语言系统声明的接口类型,代码如下(所有符合Error()string格式的方法,都能实现错误接口,Error()方法返回错误描述):
type error interface{Error() string
}

2.自定义一个错误

  • 语法:var err = errors.New(“this is an error!!!”)
  • 在代码中使用错误定义
package mainimport ("errors""fmt"
)var  myError = errors.New("this is an error!!!!!!!")
func fc(i,j int)(int, error){if(i == 0){return 0, myError}return j, nil
}
func main(){fmt.Println(fc(1,3))fmt.Println(fc(0,1))
}

结果:
在这里插入图片描述

八、函数panic和恢复recover

1.宕机Panic–程序终止运行

(1) 简单示例

  • 在Go中可以在程序中我们手动触发panic,让程序崩溃
package mainimport "fmt"func main(){fmt.Println("helllo world!!!!!!")panic("出错啦!!crash啦!!")fmt.Println("!!!!!!helllo world")
}

结果:
在这里插入图片描述

  • 上述代码中用了一个内建函数panic造成了程序崩溃,panic声明如下:func panic(v interface{}) (panic的参数可以为任意类型的)

(2) 在运行依赖的必备资源缺失时主动触发panic

  • 手动触发panic并不是一种偷懒的方式,反而能迅速报错,终止程序继续运行,防止程序发生更大的错误。
package mainimport "fmt"func main(){a := []int{0,1,2,3,4,5,6,7,8,9}i:=11for i=0;ifmt.Printf(" %d ",a[i])}fmt.Println()if i>=len(a){panic("数组越界啦!!!crash")}
}

结果:
在这里插入图片描述

(3)panic和defer的结合使用

  • 当触发panic时,panic之后的代码就不会被执行,如上述例子所示,但在panic前面已经运行过的defer语句依然会在panic发生时执行。即使defer是在函数最后执行的。注:defer语句要想执行,必须放在panic之前,如下例所示:
package mainimport "fmt"func main(){a := []int{0,1,2,3,4,5,6,7,8,9}i:=11for i=0;ifmt.Printf(" %d ",a[i])}fmt.Println()defer fmt.Println("defer要执行的语句11111")defer fmt.Println("defer要执行的语句22222")if i>=len(a){panic("数组越界啦!!!crash")}defer fmt.Println("defer要执行的语句33333不会执行")
}

结果:
在这里插入图片描述

2.宕机恢复recover–防止程序崩溃

  • recover是一个Go语言的内建函数,可以让进入panic流程中的goroutine恢复过来。recover仅会在defer函数中有效在正常的执行的过程中,调用recover会返回nil并没有其他任何效果。若当前的goroutine陷入恐慌,调用recover可捕获到panic的输入值并且恢复正常运行。常用于异常捕获
package mainimport ("fmt""runtime"
)func main(){a := []int{0,1,2,3,4,5,6,7,8,9}i:=11for i=0;ifmt.Printf(" %d ",a[i])}fmt.Println()defer fmt.Println("defer要执行的语句11111")defer fmt.Println("defer要执行的语句22222")defer func(){err := recover()switch err.(type) {case runtime.Error://运行时错误fmt.Println("运行时发生错误:::error:", err)default:fmt.Println("unknown error:::error:", err)}}()if i>=len(a){panic("数组越界啦!!!crash")}defer fmt.Println("defer要执行的语句33333不会执行")
}

结果:
在这里插入图片描述

九、计算函数执行时间

  • Go中可以使用time包中的Since()函数来获取到函数运行的时间,Now()获取到当前的时间,这样就可以计算函数运行的时间了。
package mainimport ("fmt""time"
)func fct(arr []int)(){sum := 0for i:=0; isum++}fmt.Println("结果:", sum)
}
func main(){a := []int{0,1,2,3,4,5,6,7,8,9,10}start := time.Now()//获取当前时间fct(a)res := time.Since(start)//计算出到当前时间到start的时间戳fmt.Println("该函数执行完成的耗时是:", res)
}

结果:
在这里插入图片描述

上一篇:JWT校验

下一篇:C/C++内存管管理

相关内容

热门资讯

安卓se系统怎么启用,确保应用... 你有没有发现,你的安卓手机最近有点儿“懒”呢?运行速度慢,反应迟钝,是不是想给它来个“大变身”呢?别...
微软怎么使用安卓系统,技术融合... 你有没有想过,那个以Windows系统著称的微软,竟然也会和安卓系统玩起“亲密接触”?没错,就是那个...
安卓系统耗电特别快,快速诊断与... 手机电量总是不够用?安卓系统耗电特别快,是不是你也遇到了这样的烦恼?别急,今天就来跟你聊聊这个话题,...
安卓机 桌面 系统菜单,功能解... 你有没有发现,你的安卓手机桌面系统菜单,其实就像一个隐藏的宝藏库呢?里面藏着各种各样的功能,等着你去...
安卓ios系统怎么安装,安卓与... 你有没有想过,你的手机里那些好玩的应用是怎么来的呢?是不是觉得安装个软件就像变魔术一样简单?其实,这...
珍奥助手安卓系统下载,轻松体验 你有没有听说最近有个超级好用的助手软件——珍奥助手?没错,就是那个能让你手机生活变得更加便捷的小帮手...
安卓换ios系统.数据,数据迁... 你有没有想过,手机系统就像是我们生活中的衣服,有时候换一件新衣服,整个人都焕然一新呢?没错,今天咱们...
安卓系统提示怎么关,轻松关闭功... 手机屏幕上突然弹出一个安卓系统的提示,让你不禁皱起了眉头。别急,别慌,今天就来手把手教你如何轻松关闭...
安卓系统如何刷回flyme系统... 你是不是也和我一样,对安卓手机的Flyme系统情有独钟呢?有时候,因为一些原因,我们可能需要将手机刷...
手机订餐系统源码安卓,基于手机... 你有没有想过,每天忙碌的生活中,点外卖已经成为了一种不可或缺的享受?而这一切的背后,离不开那些默默无...
顾问营销系统安卓版,助力企业高... 你有没有想过,在这个信息爆炸的时代,如何让你的产品在众多竞争者中脱颖而出呢?别急,今天我要给你介绍一...
安卓系统连接雅马哈音箱,打造个... 你有没有想过,家里的安卓手机和雅马哈音箱也能来个甜蜜的“牵手”呢?没错,今天就要来给你揭秘,如何让这...
安卓系统文件日志查看,揭秘系统... 手机里的安卓系统文件日志,听起来是不是有点儿高深莫测?别担心,今天我就要带你一探究竟,揭开这些神秘日...
努比亚升级安卓p系统,畅享智能... 你知道吗?最近手机界可是热闹非凡呢!努比亚这个品牌,竟然悄悄地给他们的手机升级了安卓P系统。这可不是...
仿苹果装安卓系统,揭秘仿苹果装... 你有没有想过,如果你的苹果手机突然变成了安卓系统,那会是怎样的场景呢?想象你那熟悉的iOS界面,突然...
安装安卓13子系统,全新功能与... 你听说了吗?安卓13子系统终于来了!这可是安卓系统的一大革新,让我们的手机体验更加丰富多元。今天,就...
安卓系统内核日志保存,深度洞察... 你有没有想过,当你手机里的安卓系统在默默运行时,它其实就像一个勤劳的小蜜蜂,不停地记录着它的“工作日...
安卓系统可以调用dll,安卓系... 你知道吗?安卓系统竟然能调用DLL文件,这可是个让人眼前一亮的小秘密呢!想象你手中的安卓设备,不仅能...
安卓通讯 录系统代码,基于安卓... 你有没有想过,你的手机里那个默默无闻的通讯录系统,其实背后有着一套复杂的代码在支撑呢?今天,就让我带...
安卓系统版本对应关系,安卓系统... 你有没有发现,每次手机更新系统,那感觉就像给手机换了个新衣裳,焕然一新呢!不过,你知道吗?安卓系统的...