【導(dǎo)讀】什么是閉包?什么場景下會用閉包?本文對 go 語言中的閉包做了詳細介紹。
閉包是由函數(shù)及其相關(guān)引用環(huán)境組合而成的實體(即:閉包=函數(shù)+引用環(huán)境)。
Go中的閉包
閉包是函數(shù)式語言中的概念,沒有研究過函數(shù)式語言的用戶可能很難理解閉包的強大,相關(guān)的概念超出了本書的范圍。Go語言是支持閉包的,這里只是簡單地講一下在Go語言中閉包是如何實現(xiàn)的。
funcf(iint)func()int{
returnfunc()int{
i++
returni
}
}
函數(shù)f返回了一個函數(shù),返回的這個函數(shù),返回的這個函數(shù)就是一個閉包。這個函數(shù)中本身是沒有定義變量i的,而是引用了它所在的環(huán)境(函數(shù)f)中的變量i。
c1:=f(0)
c2:=f(0)
c1()//referencetoi,i=0,return1
c2()//referencetoanotheri,i=0,return1
c1跟c2引用的是不同的環(huán)境,在調(diào)用i++時修改的不是同一個i,因此兩次的輸出都是1。函數(shù)f每進入一次,就形成了一個新的環(huán)境,對應(yīng)的閉包中,函數(shù)都是同一個函數(shù),環(huán)境卻是引用不同的環(huán)境。
變量i是函數(shù)f中的局部變量,假設(shè)這個變量是在函數(shù)f的棧中分配的,是不可以的。因為函數(shù)f返回以后,對應(yīng)的棧就失效了,f返回的那個函數(shù)中變量i就引用一個失效的位置了。所以閉包的環(huán)境中引用的變量不能夠在棧上分配。
escape analyze
在繼續(xù)研究閉包的實現(xiàn)之前,先看一看Go的一個語言特性:
funcf()*Cursor{
varcCursor
c.X=500
noinline()
return&c
}
Cursor是一個結(jié)構(gòu)體,這種寫法在C語言中是不允許的,因為變量c是在棧上分配的,當(dāng)函數(shù)f返回后c的空間就失效了。但是,在Go語言規(guī)范中有說明,這種寫法在Go語言中合法的。語言會自動地識別出這種情況并在堆上分配c的內(nèi)存,而不是函數(shù)f的棧上。
為了驗證這一點,可以觀察函數(shù)f生成的匯編代碼:
MOVQ$type."".Cursor+0(SB),(SP)//取變量c的類型,也就是Cursor
PCDATA$0,$16
PCDATA$1,$0
CALL,runtime.new(SB)//調(diào)用new函數(shù),相當(dāng)于new(Cursor)
PCDATA$0,$-1
MOVQ8(SP),AX//取c.X的地址放到AX寄存器
MOVQ$500,(AX)//將AX存放的內(nèi)存地址的值賦為500
MOVQAX,"".~r0+24(FP)
ADDQ$16,SP
識別出變量需要在堆上分配,是由編譯器的一種叫escape analyze的技術(shù)實現(xiàn)的。如果輸入命令:
gobuild--gcflags=-mmain.go
可以看到輸出:
./main.gomovedtoheap:c
./main.go&cescapestoheap
表示c逃逸了,被移到堆中。escape analyze可以分析出變量的作用范圍,這是對垃圾回收很重要的一項技術(shù)。
閉包結(jié)構(gòu)體
回到閉包的實現(xiàn)來,前面說過,閉包是函數(shù)和它所引用的環(huán)境。那么是不是可以表示為一個結(jié)構(gòu)體呢:
typeClosurestruct{
Ffunc()()
i*int
}
事實上,Go在底層確實就是這樣表示一個閉包的。讓我們看一下匯編代碼:
funcf(iint)func()int{
returnfunc()int{
i++
returni
}
}
MOVQ$type.int+0(SB),(SP)
PCDATA$0,$16
PCDATA$1,$0
CALL,runtime.new(SB)//是不是很熟悉,這一段就是i=new(int)
...
MOVQ$type.struct{Fuintptr;A0*int}+0(SB),(SP)//這個結(jié)構(gòu)體就是閉包的類型
...
CALL,runtime.new(SB)//接下來相當(dāng)于new(Closure)
PCDATA$0,$-1
MOVQ8(SP),AX
NOP,
MOVQ$"".func·001+0(SB),BP
MOVQBP,(AX)//函數(shù)地址賦值給Closure的F部分
NOP,
MOVQ"".&i+16(SP),BP//將堆中new的變量i的地址賦值給Closure的值部分
MOVQBP,8(AX)
MOVQAX,"".~r1+40(FP)
ADDQ$24,SP
RET,
其中func·001是另一個函數(shù)的函數(shù)地址,也就是f返回的那個函數(shù)。
小結(jié)
- Go語言支持閉包
- Go語言能通過escape analyze識別出變量的作用域,自動將變量在堆上分配。將閉包環(huán)境變量在堆上分配是Go實現(xiàn)閉包的基礎(chǔ)。
- 返回閉包時并不是單純返回一個函數(shù),而是返回了一個結(jié)構(gòu)體,記錄下函數(shù)返回地址和引用的環(huán)境中的變量地址。
tiancaiamao.gitbooks.io/go-internals/content/zh/03.6.html
-
語言
+關(guān)注
關(guān)注
1文章
97瀏覽量
24330 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4355瀏覽量
63319
原文標題:Golang 閉包的實現(xiàn)
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
EE-62:在C語言中訪問短字內(nèi)存

EE-128:C語言中的DSP:從C調(diào)用匯編類成員函數(shù)

霍爾元件常開和常閉怎么區(qū)分
深入理解C語言:循環(huán)語句的應(yīng)用與優(yōu)化技巧

C語言中申請的堆內(nèi)存能不能自動釋放
在學(xué)習(xí)go語言的過程踩過的坑
C語言中的socket編程基礎(chǔ)
go語言如何解決并發(fā)問題

c語言中從左到右結(jié)合怎么看
磁性開關(guān)常閉與常開型號區(qū)別
三十分鐘入門基礎(chǔ)Go Java小子版

技術(shù)干貨驛站 ▏深入理解C語言:基本數(shù)據(jù)類型和變量

評論