前言
Hey,大家好呀,我是星期八,上篇文章学了些基础:一篇文章带你了解Go语言基础之函数(上篇),这次咱们继续学习Go基础之函数进阶叭。
Go函数内存分配图
Go的函数内存分配,有点像堆分配,有点像,但是本质不是。
可以理解像堆内存一样,栈中保存的是堆的地址。
验证
代码
- package main
- import "fmt"
- func say() string {
- return "ok"
- }
- func main() {
- fmt.Printf("say栈上的内容:%p\n",say)
- }
结果
本质
函数的作用域
作用域这个问题,以前可能或多或少提过,再来复习一下叭。
全局变量
全局变量就是在所有函数外部定义的变量,程序不结束,变量就一直存在。
当然,任何函数都可以访问全局变量。
注:全局变量尽量全部用大写。
小试牛刀
- package main
- import "fmt"
- var NAME = "张三"
- func say() string {
- fmt.Println(NAME)
- return "ok"
- }
- func main() {
- say()
- fmt.Println(NAME)
- }
结果:
上述可能会有个问题,全局变量,全局变量,大家共用一个,要是谁傻不拉几修改了不就完蛋了,整个程序都凉了。
var引发的问题
就像这样。
- package main
- import "fmt"
- var NAME = "张三"
- func say() string {
- fmt.Println(NAME)
- NAME = "李四"
- return "ok"
- }
- func main() {
- say()
- fmt.Println(NAME)
- }
结果:
这不就完犊子了吗???所以,一定要有解决办法。
使用const解决问题
解决办法:使用常量定义全局变量。
- package main
- import "fmt"
- const NAME = "张三"
- func say() string {
- fmt.Println(NAME)
- //NAME = "李四"//会报错:cannot assign to NAME
- return "ok"
- }
- func main() {
- say()
- fmt.Println(NAME)
- }
总结
在定义全局变量时,需要用const修饰,并且变量名全部大写。
局部变量
局部变量,局部变量就是在某个函数内定义的变量,只能在自己函数内使用。
更专业点,在{}内定义的,只能在{}内使用,for同理。
代码
- package main
- import (
- "fmt"
- )
- func say() string {
- var name = "张三"
- fmt.Println(name)
- return "ok"
- }
- func main() {
- say()
- //fmt.Println(name)//会报错:undefined: name
- //for同理
- for i := 0; i <= 1; i++ {
- var c = "66"
- fmt.Println(c) //66
- }
- //fmt.Println(c)//会报错:undefined: c
- }
defer
在Go中,defer语句,可以理解为在return之前执行的一个语句。
如果函数没有return,会有一个默认的return,只是看不见而已。
一个defer
代码
- package main
- import "fmt"
- func say() {
- //defer尽量往前放
- defer fmt.Println("我是666")
- fmt.Println("你们都是最棒的")
- }
- func main() {
- say()
- }
执行结果
多个defer
代码
- package main
- import "fmt"
- func say() {
- //defer尽量往前放
- defer fmt.Println(1)
- defer fmt.Println(2)
- defer fmt.Println(3)
- fmt.Println("你们都是最棒的")
- }
- func main() {
- say()
- }
执行结果
可以发现,defer的执行结果是反着的。
结论:最先执行的defer,会最后执行,最后执行的defer,会最先执行,有点像栈,先进后出。
defer的作用
通常来说,defer会用在释放数据库连接,关闭文件等需要在函数结束时处理的操作。
这里暂时先不举例子。
panic和recover
这俩,可以理解为Python中的try和raise,因为在Go中,是没有try的,是不能像其他语言一样,try所有异常。
应用场景:比如某个web,在启动时,数据库都没连接成功,必定要启动失败,就像电脑,没有电源必不能开机一样。
panic
先看一下语法吧
- package main
- import "fmt"
- func say() {
- var flag = true
- if flag{
- //引发错误,直接中断程序的错误
- panic("OMG,撤了撤了,必须撤了")
- }
- }
- func main() {
- say()
- fmt.Println("继续呀...")//不会执行,程序挂了
- }
执行效果
可以看淡,继续呀就没打印,程序直接挂了,但是上述好像并没有解决这个问题。
recover
尝试捕捉
代码
- package main
- import "fmt"
- func say() {
- //匿名函数,defer执行的是一个匿名函数
- defer func() {
- var err = recover()
- //如果有panic错误,err!=nil,在此处步骤,尝试恢复
- if err != nil {
- fmt.Println("尝试恢复...")
- }
- }()
- var flag = true
- if flag {
- panic("OMG,撤了撤了,必须撤了")
- }
- }
- func main() {
- say()
- fmt.Println("继续呀...")
- }
执行结果
可以看到,如果recover捕捉了,并且没有panic,程序就会继续正常执行。
注意
defer必须在panic语句之前。
recover必须配合defer使用。
总结
上述我们学习了Go基础之函数进阶。如果在操作过程中有任何问题,记得下面讨论区留言,我们看到会第一时间解决问题。
本文转载自微信公众号「Go语言进阶学习」,可以通过以下二维码关注。转载本文请联系Go语言进阶学习公众号。