Golang 函数
创建函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package main import "fmt" //有参数,有返回值 func demo(a int, s string) (int, string) { return a * a, s + s } //有参数,没有返回值 func test(a int) { fmt.Println(a) } //不需要参数,有返回值 func example() string { return "this is a str be returned" } func main() { x := 10 str := "bye " x, str = demo(x, str) fmt.Println(x, str) //100 bye bye y := 99 test(y) //99 //有返回值,但不接收返回值 example() //接收返回值 s := example() fmt.Println(s) //this is a str be returned } |
可变参数
如果一个函数的参数个数是可变的,那么可以在定义函数时,在参数列表的最后一个参数类型之前加上...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package main import "fmt" func sum(num ...int) int { res := 0 for _, v := range num { res += v } return res } func multi_params(a string, x int, y ...int) { fmt.Println( "第一个参数:" , a) fmt.Println( "第二个参数:" , x) fmt.Println( "可变参数:" , y) } func main() { tot := sum(1, 2, 3, 4, 5) fmt.Println(tot) //15 multi_params( "hello" , 99, 1, 2, 3, 4) //第一个参数: hello //第二个参数: 99 //可变参数: [1 2 3 4] } |
函数多返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package main import "fmt" //返回值可直接写返回值的类型,如果是单个返回值,可省略返回值的括号 //参数列表中,如果参数类型都相同,可以只在最后写一次类型 func swap(a, b int) (int, int) { return b, a } //可以指定返回值的名称,这个名称只是为了方便理解,并没有什么用 func compute(a int, b int) (sum, sub int) { return a + b, a - b } func main() { a := 10 b := 20 a, b = swap(a, b) fmt.Println(a, b) //20 10 x, y := compute(10, 5) fmt.Println(x, y) //15 5 } |
错误处理 defer
defer后面的函数在Go程序设计语言中被称为延迟函数,为什么叫延迟呢?因为这个defer函数的存在于一个函数之中(包括main函数),defer函数执行的时机是在 包含他的那个函数(上一层函数)将要执行完之前的最后一步,即函数最后一条语句执行完了,然后在执行这个defer函数,如果同一个函数内部有多个defer的函数,那么,这些个defer函数的执行顺序按照栈的后进先出,最后定义的defer函数最先执行,最先定义的defer函数最后执行。
先看一个简单的例子:
1 2 3 4 5 6 7 8 9 10 | package main import "fmt" func main(){ defer func (){ fmt.Println( "run last" ) }() //注意defer的函数后面一定要加圆括号,不然就是定义函数,而不会执行函数 fmt.Println( "run first" ) } |
在上面这个例子中,包含defer函数的 函数是main函数,所以在main()函数执行完毕之后(不能说main函数执行完毕,应该说已经顺序执行完除了defer函数以外的所有语句),才执行defer函数,所以运行结果如下:
1 2 | run first run last |
然后看下面这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package main import "fmt" func test(){ fmt.Println( "One" ) defer func (){ fmt.Println( "two" ) }() defer func (){ fmt.Println( "three" ) }() } func main(){ test() fmt.Println( "run first" ) } |
上面这个例子中test函数包含在main()中,test函数中包含两个延迟函数,所以正常语句执行完之后,应该先执行第二个延迟函数,然后执行第一个延迟函数,然后test函数执行完毕后,在顺序执行test函数后面的语句。
1 2 3 4 | One three two run first |
再看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package main import "fmt" func test(){ fmt.Println( "One" ) defer func (){ fmt.Println( "two" ) }() defer func (){ fmt.Println( "three" ) }() } func main(){ defer test() #与上面一个例子的区别,这里test函数是延迟函数 fmt.Println( "run first" ) } |
因为test函数此时作为延迟函数,那么test函数在main函数中最后执行,执行test的时候,因为test中又有延迟函数,仍旧遵守上面的顺序,所以执行的结果如下:
1 2 3 4 | run first One three two |
延迟函数类似于其他面向对象语言中的析构函数,只是很类似,不要混为一谈。延迟通常用来做一些收尾工作,给他的主子擦屁股,比如关闭打开的文件,或者一些连接状态,亦或者清楚一些数据。。。。。
defer的注意事项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package main import "fmt" func main() { i := 0 defer fmt.Println( "调用defer1,i=" , i) defer func () { fmt.Println( "调用defere2,i=" , i) }() for i = 0; i < 5; i++ { fmt.Print( " " , i) } fmt.Println() } // 0 1 2 3 4 //调用defere2,i= 5 //调用defer1,i= 0 |
上面第一个defer中的i,其实是在循环开始之前,就已经将i的值传递给fmt.Println了。
类似于下面这个写法
1 2 3 4 | i := 0 defer func (x int) { fmt.Println( "调用defer1,i=" , x) }(i) |
函数变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package main import "fmt" //定义新的类型,为函数类型,后面是函数签名 type handler func (int, int) int func main() { foo := func (a, b int) (sum int) { return a + b } fmt.Println(foo(3, 5)) //8 demo := foo fmt.Println(demo(3, 3)) //6 var test handler test = demo fmt.Println(test(4, 4)) } |
闭包
即函数(外部函数)中再定义一个函数(内部函数),并且将内部函数返回,对,返回一个函数,内部函数就叫做闭包。在内部函数中可以调用外部函数中的变量。并且在外部函数调用完之后,外部函数中定义的局部变量并不会立即销毁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package main import "fmt" //外部函数需要参数 a int,b int //返回值为一个函数,该函数需要一个参数int型的time倍数 ,并返回一个int型的值 func demo(a, b int) func (time int) int { sum := a + b //求和 return func (t int) int { return sum * t } } func main() { foo := demo(3, 5) //此时foo是一个函数 fmt.Println(foo) //0x10936b0 指向内部函数的地址 fmt.Println(foo(4)) //32 } |
第二个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package main import "fmt" func demo() func () int { cnt := 0 return func () int { cnt++ return cnt } } func main() { foo := demo() //调用demo之后,demo函数内部的局部变量并不会立即销毁,会一直保存 fmt.Println(foo()) //1 fmt.Println(foo()) //2 } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 手把手教你更优雅的享受 DeepSeek
· AI工具推荐:领先的开源 AI 代码助手——Continue
· 探秘Transformer系列之(2)---总体架构
· V-Control:一个基于 .NET MAUI 的开箱即用的UI组件库
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现