一篇文章帶你了解Go語言基礎之函數(中篇)
前言
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語言進階學習公眾號。