首頁  >  文章  >  後端開發  >  一篇文章帶你了解Go語言基礎之函數(中篇)

一篇文章帶你了解Go語言基礎之函數(中篇)

Go语言进阶学习
Go语言进阶学习轉載
2023-07-25 14:17:311280瀏覽

Go函數記憶體分配圖

Go的函數記憶體分配,有點像堆分配,有點像,但本質不是。

一篇文章帶你了解Go語言基礎之函數(中篇)

可以理解像堆疊記憶體一樣,堆疊中保存的是堆疊的位址。

驗證

#
package main


import "fmt"




func say() string {
    return "ok"
}


func main() {
    fmt.Printf("say栈上的内容:%p\n",say)
}

結果

一篇文章帶你了解Go語言基礎之函數(中篇)

#本質

一篇文章帶你了解Go語言基礎之函數(中篇)

函數的作用域

作用域這個問題,以前可能或多或少提過,再來複習一下收回。

全域變數

全域變數就是在所有函數外部定義的變量,程式不結束,變數就一直存在。

当然,任何函数都可以访问全局变量。

注:全局变量尽量全部用大写。

小试牛刀

package main


import "fmt"




var NAME = "张三"
func say() string {
    fmt.Println(NAME)
    return "ok"
}


func main() {
    say()
    fmt.Println(NAME)
}

结果:

一篇文章帶你了解Go語言基礎之函數(中篇)

上述可能会有个问题,全局变量,全局变量,大家共用一个,要是谁傻不拉几修改了不就完蛋了,整个程序都凉了。

var引发的问题

就像这样。

package main


import "fmt"


var NAME = "张三"


func say() string {
    fmt.Println(NAME)
    NAME = "李四"
    return "ok"
}


func main() {
    say()
    fmt.Println(NAME)
}

结果:

一篇文章帶你了解Go語言基礎之函數(中篇)

这不就完犊子了吗???所以,一定要有解决办法。

使用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()
}

执行结果

一篇文章帶你了解Go語言基礎之函數(中篇)

多个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()
}

执行结果

一篇文章帶你了解Go語言基礎之函數(中篇)

可以发现,defer的执行结果是反着的。

结论:先执行的defer,会最后执行,最后执行的defer,会最先执行,有点像栈,先进后出

defer的作用

通常来说,defer会用在释放数据库连接,关闭文件等需要在函数结束时处理的操作。

这里暂时先不举例子。


panic和recover

这俩,可以理解为Python中的tryraise,因为在Go中,是没有try的,是不能像其他语言一样,try所有异常。

应用场景:比如某个web,在启动时,数据库都没连接成功,必定要启动失败,就像电脑,没有电源必不能开机一样。

panic

先看一下语法吧

package main


import "fmt"


func say() {
    var flag = true
    if flag{
        //引发错误,直接中断程序的错误
        panic("OMG,撤了撤了,必须撤了")
}
}


func main() {
    say()
    fmt.Println("继续呀...")//不会执行,程序挂了
}

执行效果

一篇文章帶你了解Go語言基礎之函數(中篇)

可以看淡,继续呀就没打印,程序直接挂了,但是上述好像并没有解决这个问题。

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("继续呀...")
}

执行结果

一篇文章帶你了解Go語言基礎之函數(中篇)

可以看到,如果recover捕捉了,并且没有panic,程序就会继续正常执行。

注意

defer必须在panic语句之前。

recover必须配合defer使用。

以上是一篇文章帶你了解Go語言基礎之函數(中篇)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:Go语言进阶学习。如有侵權,請聯絡admin@php.cn刪除