一篇文章帶你了解Go語言基礎(chǔ)之函數(shù)(中篇)
回復(fù)“Go語言”即可獲贈Python從入門到進階共10本電子書
前言
Hey,大家好呀,我是星期八,上篇文章學(xué)了些基礎(chǔ):一篇文章帶你了解Go語言基礎(chǔ)之函數(shù)(上篇),這次咱們繼續(xù)學(xué)習(xí)Go基礎(chǔ)之函數(shù)進階叭。
Go函數(shù)內(nèi)存分配圖
Go的函數(shù)內(nèi)存分配,有點像堆分配,有點像,但是本質(zhì)不是。

可以理解像堆內(nèi)存一樣,棧中保存的是堆的地址。
驗證
代碼
package mainimport "fmt"func say() string {return "ok"}func main() {fmt.Printf("say棧上的內(nèi)容:%p\n",say)}
結(jié)果

本質(zhì)

函數(shù)的作用域
作用域這個問題,以前可能或多或少提過,再來復(fù)習(xí)一下叭。
全局變量
全局變量就是在所有函數(shù)外部定義的變量,程序不結(jié)束,變量就一直存在。
當(dāng)然,任何函數(shù)都可以訪問全局變量。
注:全局變量盡量全部用大寫。
小試牛刀
package mainimport "fmt"var NAME = "張三"func say() string {fmt.Println(NAME)return "ok"}func main() {say()fmt.Println(NAME)}
結(jié)果:

上述可能會有個問題,全局變量,全局變量,大家共用一個,要是誰傻不拉幾修改了不就完蛋了,整個程序都涼了。
var引發(fā)的問題
就像這樣。
package mainimport "fmt"var NAME = "張三"func say() string {fmt.Println(NAME)NAME = "李四"return "ok"}func main() {say()fmt.Println(NAME)}
結(jié)果:

這不就完犢子了嗎???所以,一定要有解決辦法。
使用const解決問題
解決辦法:使用常量定義全局變量。
package mainimport "fmt"const NAME = "張三"func say() string {fmt.Println(NAME)//NAME = "李四"http://會報錯:cannot assign to NAMEreturn "ok"}func main() {say()fmt.Println(NAME)}
總結(jié)
在定義全局變量時,需要用const修飾,并且變量名全部大寫。
局部變量
局部變量,局部變量就是在某個函數(shù)內(nèi)定義的變量,只能在自己函數(shù)內(nèi)使用。
更專業(yè)點,在{}內(nèi)定義的,只能在{}內(nèi)使用,for同理。
代碼
package mainimport ("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之前執(zhí)行的一個語句。
如果函數(shù)沒有return,會有一個默認的return,只是看不見而已。
一個defer
代碼
package mainimport "fmt"func say() {//defer盡量往前放defer fmt.Println("我是666")fmt.Println("你們都是最棒的")}func main() {say()}
執(zhí)行結(jié)果

多個defer
代碼
package mainimport "fmt"func say() {//defer盡量往前放defer fmt.Println(1)defer fmt.Println(2)defer fmt.Println(3)fmt.Println("你們都是最棒的")}func main() {say()}
執(zhí)行結(jié)果

可以發(fā)現(xiàn),defer的執(zhí)行結(jié)果是反著的。
結(jié)論:最先執(zhí)行的defer,會最后執(zhí)行,最后執(zhí)行的defer,會最先執(zhí)行,有點像棧,先進后出。
defer的作用
通常來說,defer會用在釋放數(shù)據(jù)庫連接,關(guān)閉文件等需要在函數(shù)結(jié)束時處理的操作。
這里暫時先不舉例子。
panic和recover
這倆,可以理解為Python中的try和raise,因為在Go中,是沒有try的,是不能像其他語言一樣,try所有異常。
應(yīng)用場景:比如某個web,在啟動時,數(shù)據(jù)庫都沒連接成功,必定要啟動失敗,就像電腦,沒有電源必不能開機一樣。
panic
先看一下語法吧
package mainimport "fmt"func say() {var flag = trueif flag{//引發(fā)錯誤,直接中斷程序的錯誤panic("OMG,撤了撤了,必須撤了")}}func main() {say()fmt.Println("繼續(xù)呀...")//不會執(zhí)行,程序掛了}
執(zhí)行效果

可以看淡,繼續(xù)呀就沒打印,程序直接掛了,但是上述好像并沒有解決這個問題。
recover
嘗試捕捉
代碼
package mainimport "fmt"func say() {//匿名函數(shù),defer執(zhí)行的是一個匿名函數(shù)defer func() {var err = recover()//如果有panic錯誤,err!=nil,在此處步驟,嘗試恢復(fù)if err != nil {fmt.Println("嘗試恢復(fù)...")}}()var flag = trueif flag {panic("OMG,撤了撤了,必須撤了")}}func main() {say()fmt.Println("繼續(xù)呀...")}
執(zhí)行結(jié)果

可以看到,如果recover捕捉了,并且沒有panic,程序就會繼續(xù)正常執(zhí)行。
注意
defer必須在panic語句之前。
recover必須配合defer使用。
總結(jié)
上述我們學(xué)習(xí)了Go基礎(chǔ)之函數(shù)進階。如果在操作過程中有任何問題,記得下面討論區(qū)留言,我們看到會第一時間解決問題。
我是碼農(nóng)星期八,如果覺得還不錯,記得動手點贊一下哈。感謝你的觀看。
-------------------?End?-------------------

歡迎大家點贊,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持
想加入學(xué)習(xí)群請在后臺回復(fù)【入群】
萬水千山總是情,點個【在看】行不行
