什么是defer
創(chuàng)新互聯(lián)建站主營(yíng)建湖網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,app軟件定制開(kāi)發(fā),建湖h5小程序定制開(kāi)發(fā)搭建,建湖網(wǎng)站營(yíng)銷(xiāo)推廣歡迎建湖等地區(qū)企業(yè)咨詢
defer 可以保證方法可以在外圍函數(shù)返回之前調(diào)用。有點(diǎn)像其他言的 try finally
Go語(yǔ)言defer預(yù)計(jì)算參數(shù)
Go 語(yǔ)言中所有的函數(shù)調(diào)用都是傳值的,雖然 defer 是關(guān)鍵字,但是也繼承了這個(gè)特性。假設(shè)我們想要計(jì)算 main 函數(shù)運(yùn)行的時(shí)間,可能會(huì)寫(xiě)出以下的代碼:
結(jié)果是:
運(yùn)行結(jié)果并不符合我們的預(yù)期,這個(gè)現(xiàn)象背后的原因是什么呢?經(jīng)過(guò)分析,我們會(huì)發(fā)現(xiàn)調(diào)用 defer 關(guān)鍵字會(huì)立刻拷貝函數(shù)中引用的外部參數(shù),所以 time.Since(startedAt) 的結(jié)果不是在 main 函數(shù)退出之前計(jì)算的,而是在 defer 關(guān)鍵字調(diào)用時(shí)計(jì)算的【defer入棧的時(shí)候】,最終導(dǎo)致上述代碼輸出 0s
我們?cè)賮?lái)看個(gè)簡(jiǎn)單例子來(lái)說(shuō)明上述解釋:
當(dāng)代碼運(yùn)行到defer fmt.Println(test(i))的時(shí)候,會(huì)把defer右邊最外層函數(shù)的參數(shù)計(jì)算完畢,并傳遞進(jìn)函數(shù)里,但不會(huì)執(zhí)行函數(shù)體的代碼直到包裹defer的函數(shù)返回。我們先看會(huì)把defer右邊最外層函數(shù)的參數(shù)計(jì)算完畢,并傳遞進(jìn)函數(shù)里這句話,對(duì)應(yīng)例子就是先把test(i)算出來(lái),此時(shí)i=1,計(jì)算test(1)得2,然后fmt.Println(2)入棧,等到最后程序運(yùn)行完了再運(yùn)行defer結(jié)果就是2(但不會(huì)執(zhí)行函數(shù)體的代碼直到包裹defer的函數(shù)返回)。
我們?cè)賮?lái)看一個(gè)例子與匿名函數(shù)結(jié)合:
結(jié)果:
使用匿名函數(shù),結(jié)果是101,相當(dāng)于i給到test方法的是100,那為什么呢?還是那句話:但不會(huì)執(zhí)行函數(shù)體的代碼直到包裹defer的函數(shù)返回
也就是說(shuō)他會(huì)把整個(gè){ fmt.Println(test(i)) }()函數(shù)體入棧,等到最后程序運(yùn)行完了再運(yùn)行defer,此時(shí)的i是100,運(yùn)行test后就是101了。
所以你要解決第一個(gè)打印為0s的問(wèn)題,你就可以使用匿名函數(shù)來(lái)解決,如下:
結(jié)果:
函數(shù)的go語(yǔ)言中的一級(jí)公民,我們把所有的功能單元都定義在函數(shù)中,可以重復(fù)使用。函數(shù)包含函數(shù)的名稱、參數(shù)列表和返回值類型,這些構(gòu)成了函數(shù)的簽名(signature)。
函數(shù)在使用之前必須先定義,可以調(diào)用函數(shù)來(lái)完成某個(gè)任務(wù)。函數(shù)可以重復(fù)調(diào)用,從而達(dá)到代碼重用。
go語(yǔ)言函數(shù)定義語(yǔ)法
語(yǔ)法解析:
go語(yǔ)言函數(shù)定義實(shí)例
定義一個(gè)求和函數(shù)
定義一個(gè)比較兩個(gè)數(shù)大小的函數(shù)
go語(yǔ)言函數(shù)調(diào)用
當(dāng)我們要完成某個(gè)任務(wù)時(shí),可以調(diào)用函數(shù)來(lái)完成。調(diào)用函數(shù)要傳遞參數(shù),如何有返回值可以獲得返回值。
運(yùn)行結(jié)果
GO是編譯性語(yǔ)言,所以函數(shù)的順序是無(wú)關(guān)緊要的,為了方便閱讀,建議入口函數(shù) main 寫(xiě)在最前面,其余函數(shù)按照功能需要進(jìn)行排列
GO的函數(shù) 不支持嵌套,重載和默認(rèn)參數(shù)
GO的函數(shù) 支持 無(wú)需聲明變量,可變長(zhǎng)度,多返回值,匿名,閉包等
GO的函數(shù)用 func 來(lái)聲明,且左大括號(hào) { 不能另起一行
一個(gè)簡(jiǎn)單的示例:
輸出為:
參數(shù):可以傳0個(gè)或多個(gè)值來(lái)供自己用
返回:通過(guò)用 return 來(lái)進(jìn)行返回
輸出為:
上面就是一個(gè)典型的多參數(shù)傳遞與多返回值
對(duì)例子的說(shuō)明:
按值傳遞:是對(duì)某個(gè)變量進(jìn)行復(fù)制,不能更改原變量的值
引用傳遞:相當(dāng)于按指針傳遞,可以同時(shí)改變?cè)瓉?lái)的值,并且消耗的內(nèi)存會(huì)更少,只有4或8個(gè)字節(jié)的消耗
在上例中,返回值 (d int, e int, f int) { 是進(jìn)行了命名,如果不想命名可以寫(xiě)成 (int,int,int){ ,返回的結(jié)果都是一樣的,但要注意:
當(dāng)返回了多個(gè)值,我們某些變量不想要,或?qū)嶋H用不到,我們可以使用 _ 來(lái)補(bǔ)位,例如上例的返回我們可以寫(xiě)成 d,_,f := test(a,b,c) ,我們不想要中間的返回值,可以以這種形式來(lái)舍棄掉
在參數(shù)后面以 變量 ... type 這種形式的,我們就要以判斷出這是一個(gè)可變長(zhǎng)度的參數(shù)
輸出為:
在上例中, strs ...string 中, strs 的實(shí)際值是b,c,d,e,這就是一個(gè)最簡(jiǎn)單的傳遞可變長(zhǎng)度的參數(shù)的例子,更多一些演變的形式,都非常類似
在GO中 defer 關(guān)鍵字非常重要,相當(dāng)于面相對(duì)像中的析構(gòu)函數(shù),也就是在某個(gè)函數(shù)執(zhí)行完成后,GO會(huì)自動(dòng)這個(gè);
如果在多層循環(huán)中函數(shù)里,都定義了 defer ,那么它的執(zhí)行順序是先進(jìn)后出;
當(dāng)某個(gè)函數(shù)出現(xiàn)嚴(yán)重錯(cuò)誤時(shí), defer 也會(huì)被調(diào)用
輸出為
這是一個(gè)最簡(jiǎn)單的測(cè)試了,當(dāng)然還有更復(fù)雜的調(diào)用,比如調(diào)試程序時(shí),判斷是哪個(gè)函數(shù)出了問(wèn)題,完全可以根據(jù) defer 打印出來(lái)的內(nèi)容來(lái)進(jìn)行判斷,非常快速,這種留給你們?nèi)?shí)現(xiàn)
一個(gè)函數(shù)在函數(shù)體內(nèi)自己調(diào)用自己我們稱之為遞歸函數(shù),在做遞歸調(diào)用時(shí),經(jīng)常會(huì)將內(nèi)存給占滿,這是非常要注意的,常用的比如,快速排序就是用的遞歸調(diào)用
本篇重點(diǎn)介紹了GO函數(shù)(func)的聲明與使用,下一篇將介紹GO的結(jié)構(gòu) struct
按值傳遞函數(shù)參數(shù),是拷貝參數(shù)的實(shí)際值到函數(shù)的形式參數(shù)的方法調(diào)用。在這種情況下,參數(shù)在函數(shù)內(nèi)變化對(duì)參數(shù)不會(huì)有影響。
默認(rèn)情況下,Go編程語(yǔ)言使用調(diào)用通過(guò)值的方法來(lái)傳遞參數(shù)。在一般情況下,這意味著,在函數(shù)內(nèi)碼不能改變用來(lái)調(diào)用所述函數(shù)的參數(shù)。考慮函數(shù)swap()的定義如下。
代碼如下:
/* function definition to swap the values */
func swap(int x, int y) int {
var temp int
temp = x /* save the value of x */
x = y /* put y into x */
y = temp /* put temp into y */
return temp;
}
現(xiàn)在,讓我們通過(guò)使實(shí)際值作為在以下示例調(diào)用函數(shù)swap():
代碼如下:
package main
import "fmt"
func main() {
/* local variable definition */
var a int = 100
var b int = 200
fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b )
/* calling a function to swap the values */
swap(a, b)
fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x, y int) int {
var temp int
temp = x /* save the value of x */
x = y /* put y into x */
y = temp /* put temp into y */
return temp;
}
讓我們把上面的代碼放在一個(gè)C文件,編譯并執(zhí)行它,它會(huì)產(chǎn)生以下結(jié)果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :100
After swap, value of b :200
這表明,參數(shù)值沒(méi)有被改變,雖然它們已經(jīng)在函數(shù)內(nèi)部改變。
通過(guò)傳遞函數(shù)參數(shù),即是拷貝參數(shù)的地址到形式參數(shù)的參考方法調(diào)用。在函數(shù)內(nèi)部,地址是訪問(wèn)調(diào)用中使用的實(shí)際參數(shù)。這意味著,對(duì)參數(shù)的更改會(huì)影響傳遞的參數(shù)。
要通過(guò)引用傳遞的值,參數(shù)的指針被傳遞給函數(shù)就像任何其他的值。所以,相應(yīng)的,需要聲明函數(shù)的參數(shù)為指針類型如下面的函數(shù)swap(),它的交換兩個(gè)整型變量的值指向它的參數(shù)。
代碼如下:
/* function definition to swap the values */
func swap(x *int, y *int) {
var temp int
temp = *x /* save the value at address x */
*x = *y /* put y into x */
*y = temp /* put temp into y */
}
現(xiàn)在,讓我們調(diào)用函數(shù)swap()通過(guò)引用作為在下面的示例中傳遞數(shù)值:
代碼如下:
package main
import "fmt"
func main() {
/* local variable definition */
var a int = 100
var b int= 200
fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b )
/* calling a function to swap the values.
* a indicates pointer to a ie. address of variable a and
* b indicates pointer to b ie. address of variable b.
*/
swap(a, b)
fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* save the value at address x */
*x = *y /* put y into x */
*y = temp /* put temp into y */
}
讓我們把上面的代碼放在一個(gè)C文件,編譯并執(zhí)行它,它會(huì)產(chǎn)生以下結(jié)果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :200
After swap, value of b :100
這表明變化的功能以及不同于通過(guò)值調(diào)用的外部體現(xiàn)的改變不能反映函數(shù)之外。
在以下這段代碼中,我們操作一個(gè)文件,無(wú)論成功與否都需要關(guān)閉文件句柄。這里在三處不同的位置都調(diào)用了file.Close()方法,代碼顯得非常冗余。
我們利用延遲調(diào)用來(lái)優(yōu)化代碼。定義后的defer代碼,會(huì)在return之前返回,讓代碼顯得更加緊湊,且可讀性變強(qiáng),對(duì)上面的代碼改造如下:
我們通過(guò)這個(gè)示例來(lái)看一下延遲調(diào)用與正常代碼之間的執(zhí)行順序
先簡(jiǎn)單分析一下代碼邏輯:
從輸出中,我們可以觀察到如下現(xiàn)象:
從這個(gè)實(shí)例中,我們很明顯觀察到,defer語(yǔ)句是在return之前執(zhí)行
如果一個(gè)函數(shù)內(nèi)定義了多個(gè)defer,則調(diào)用順序?yàn)長(zhǎng)IFO(后進(jìn)先出)方式執(zhí)行。
仍然是相同的例子,但是在TestDefer中我們定義了三個(gè)defer輸出,根據(jù)LIFO原則,輸出的順序是3rd-2nd-1st,根據(jù)最后的結(jié)果,也是逆向向上執(zhí)行defer輸出。
就在整理這篇筆記的時(shí)候,發(fā)現(xiàn)了自己的認(rèn)知誤區(qū),主要是本節(jié)實(shí)例三中發(fā)現(xiàn)的,先來(lái)看一下英文的描述:
對(duì)于上面的這段話的理解:
下面是代碼執(zhí)行輸出,我們來(lái)一起分析一下:
雖然在a()函數(shù)內(nèi),顯示的返回了10,但是main函數(shù)中得到的結(jié)果是defer函數(shù)自增后的結(jié)果,我們來(lái)分析一下代碼:
在這篇文章的上一版,我曾經(jīng)嘗試用指針取解釋defer修改返回值的類型,但是感覺(jué)不夠透徹,也讓閱讀者非常困惑,索性參考了一下go官方blog中的一篇文章,在此基礎(chǔ)上進(jìn)行了擴(kuò)展。如需要閱讀原文,可以參考下面的文章。
前段時(shí)間,我們實(shí)驗(yàn)室用go作為后臺(tái)開(kāi)發(fā)語(yǔ)言開(kāi)發(fā)了一個(gè)web項(xiàng)目,由于這是自己第一次使用go語(yǔ)言進(jìn)行開(kāi)發(fā),在開(kāi)發(fā)過(guò)程中,一味著追求完成任務(wù),在編碼的時(shí)候沒(méi)有太注重性能,雖然勉強(qiáng)實(shí)現(xiàn)了功能,但是對(duì)go語(yǔ)言的理解還是比較淺顯的。下面來(lái)談?wù)勛约簩?duì)go語(yǔ)言中函數(shù)與方法的理解。
普通函數(shù):
go函數(shù)可以返回多個(gè)值
值傳遞: 值傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)復(fù)制一份傳遞到函數(shù)中,這樣函數(shù)中如果對(duì)參數(shù)進(jìn)行修改,將不會(huì)影響到實(shí)際參數(shù)
引用傳遞: 引用傳遞是指在調(diào)用函數(shù)將實(shí)際參數(shù)的地址傳遞到函數(shù)中,那么在函數(shù)中對(duì)參數(shù)進(jìn)行的修改,將影響到實(shí)際參數(shù)。
一般來(lái)說(shuō)go語(yǔ)言函數(shù)的 接收者(也就是形參)一般放在函數(shù)名后面 ,不能將指針類型的數(shù)據(jù)直接傳遞,也就是說(shuō)函數(shù)形參如果是值類型,調(diào)用者必須使用值作為實(shí)參過(guò)來(lái),如果函數(shù)形參是指針類型,則函數(shù)調(diào)用者需使用指針作為實(shí)參來(lái)調(diào)用。
普通方法:
接收者是在func關(guān)鍵字后面,而不是在函數(shù)名稱后面,接收者可以是自己定義的一個(gè)類型,這個(gè)類型可以是struct、interface,一個(gè)方法就是一個(gè)包含了接收者的函數(shù),接收者可以是命名類型或者是結(jié)構(gòu)體類型的一個(gè)值或者是一個(gè)指針。
下面是一個(gè)例子來(lái)說(shuō)明方法和函數(shù)的區(qū)別(重點(diǎn))
本文題目:go語(yǔ)言函數(shù)調(diào)用時(shí)間 go語(yǔ)言常用函數(shù)
文章網(wǎng)址:http://chinadenli.net/article34/dodicpe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、網(wǎng)站設(shè)計(jì)公司、ChatGPT、企業(yè)網(wǎng)站制作、網(wǎng)站排名、軟件開(kāi)發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容