最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go 中閉包的底層原理

 更新時間:2021年10月27日 14:28:34   作者:寫代碼的明哥  
這篇文章主要介紹了Go 中閉包的底層原理,閉包的基本原理是一種現(xiàn)象,一個函數(shù)內引用了外部的局部變量的現(xiàn)象,帶著些許的了解和小編一起進入文章正題學習

1. 什么是閉包?

一個函數(shù)內引用了外部的局部變量,這種現(xiàn)象,就稱之為閉包。

例如下面的這段代碼中,adder 函數(shù)返回了一個匿名函數(shù),而該匿名函數(shù)中引用了 adder 函數(shù)中的局部變量 sum ,那這個函數(shù)就是一個閉包。

package main 
 
import "fmt" 
 
func adder() func(int) int { 
    sum := 0 
    return func(x int) int { 
        sum += x 
        return sum 
    } 
} 

而這個閉包中引用的外部局部變量并不會隨著 adder 函數(shù)的返回而被從棧上銷毀。

我們嘗試著調用這個函數(shù),發(fā)現(xiàn)每一次調用,sum 的值都會保留在 閉包函數(shù)中以待使用。

func main() { 
     valueFunc:= adder() 
     fmt.Println(valueFunc(2))     // output: 2 
     fmt.Println(valueFunc(2))   // output: 4 
} 

2. 復雜的閉包場景

寫一個閉包是比較容易的事,但單單會寫簡單的閉包函數(shù),還遠遠不夠,如果不搞清楚閉包真正的原理,那很容易在一些復雜的閉包場景中對函數(shù)的執(zhí)行邏輯進行誤判。

別的不說,就拿下來這個例子來說吧?

你覺得它會打印什么呢?

是 6 還是 11 呢?

import "fmt" 
 
func func1() (i int) { 
    i = 10 
    defer func() { 
        i += 1 
    }() 
    return 5 
} 
 
func main() { 
    closure := func1() 
    fmt.Println(closure) 
} 

3. 閉包的底層原理?

還是以最上面的例子來分析

package main 
 
import "fmt" 
 
func adder() func(int) int { 
    sum := 0 
    return func(x int) int { 
        sum += x 
        return sum 
    } 
} 
 
func main() { 
    valueFunc:= adder() 
    fmt.Println(valueFunc(2))     // output: 2 
} 

我們先對它進行逃逸分析,很容易發(fā)現(xiàn) sum 作為 adder 函數(shù)局部變量,并不是分配在棧上,而是分配在堆上的。

這就解決了第一個疑惑:為什么 adder 函數(shù)返回后, sum 不會隨之銷毀?

$ go build -gcflags="-m -m -l" demo.go 
# command-line-arguments 
./demo.go:8:3: adder.func1 capturing by ref: sum (addr=true assign=true width=8) 
./demo.go:7:9: func literal escapes to heap: 
./demo.go:7:9:   flow: ~r0 = &{storage for func literal}: 
./demo.go:7:9:     from func literal (spill) at ./demo.go:7:9 
./demo.go:7:9:     from return func literal (return) at ./demo.go:7:2 
./demo.go:6:2: sum escapes to heap: 
./demo.go:6:2:   flow: {storage for func literal} = &sum: 
./demo.go:6:2:     from func literal (captured by a closure) at ./demo.go:7:9 
./demo.go:6:2:     from sum (reference) at ./demo.go:8:3 
./demo.go:6:2: moved to heap: sum 
./demo.go:7:9: func literal escapes to heap 
./demo.go:15:23: valueFunc(2) escapes to heap: 
./demo.go:15:23:   flow: {storage for ... argument} = &{storage for valueFunc(2)}: 
./demo.go:15:23:     from valueFunc(2) (spill) at ./demo.go:15:23 
./demo.go:15:23:   flow: {heap} = {storage for ... argument}: 
./demo.go:15:23:     from ... argument (spill) at ./demo.go:15:13 
./demo.go:15:23:     from fmt.Println(valueFunc(2)) (call parameter) at ./demo.go:15:13 
./demo.go:15:13: ... argument does not escape 
./demo.go:15:23: valueFunc(2) escapes to heap 

可另一個問題,又浮現(xiàn)出來了,就算它不會銷毀,那閉包函數(shù)若是存儲的若是 sum 拷貝后的值,那每次調用閉包函數(shù),里面的 sum 應該都是一樣的,調用兩次都應該返回 2,而不是可以累加記錄。

因此,可以大膽猜測,閉包函數(shù)的結構體里存儲的是 sum 的指針。

為了驗證這一猜想,只能上匯編了。

通過執(zhí)行下面的命令,可以輸出對應的匯編代碼

go build -gcflags="-S" demo.go  

輸出的內容相當之多,我提取出下面最關鍵的一行代碼,它定義了閉包函數(shù)的結構體。

其中 F 是函數(shù)的指針,但這不是重點,重點是 sum 存儲的確實是指針,驗證了我們的猜。

type.noalg.struct { F uintptr; "".sum *int }(SB), CX 

4. 迷題揭曉

有了上面第三節(jié)的背景知識,那對于第二節(jié)給出的這道題,想必你也有答案了。

首先,由于 i 在函數(shù)定義的返回值上聲明,因此根據(jù) go 的 caller-save 模式, i 變量會存儲在 main 函數(shù)的棧空間。

然后,func1 return 重新把 5 賦值給了 i ,此時 i = 5

由于閉包函數(shù)存儲了這個變量 i 的指針。

因此最后,在 defer 中對 i 進行自增,是直接更新到 i 的指針上,此時 i = 5+1,所以最終打印出來的結果是 6

import "fmt" 
 
func func1() (i int) { 
    i = 10 
    defer func() { 
        i += 1 
    }() 
    return 5 
} 
 
func main() { 
    closure := func1() 
    fmt.Println(closure) 
} 

5. 再度變題

上面那題聽懂了的話,再來看看下面這道題。

func1 的返回值我們不寫變量名 i 了,然后原先返回具體字面量,現(xiàn)在改成變量 i ,就是這兩小小小的改動,會導致運行結果大大不同,你可以思考一下結果。

import "fmt" 
 
func func1() (int) { 
    i := 10 
    defer func() { 
        i += 1 
    }() 
    return i 
} 
 
func main() { 
    closure := func1() 
    fmt.Println(closure) 
} 

如果你在返回值里寫了變量名,那么該變量會存儲 main 的棧空間里,而如果你不寫,那 i 只能存儲在 func1 的??臻g里,與此同時,return 的值,不會作用于原變量 i 上,而是會存儲在該函數(shù)在另一塊棧內存里。

因此你在 defer 中對原 i 進行自增,并不會作用到 func1 的返回值上。

所以打印的結果,只能是 10。

你答對了嗎?

6. 最后一個問題

不知道你有沒有發(fā)現(xiàn),在第一節(jié)示例中的 sum 是存儲在堆內存中的,而后面幾個示例都是存儲在棧內存里。

這是為什么呢?

仔細對比,不難發(fā)現(xiàn),示例一返回的是閉包函數(shù),閉包函數(shù)在 adder 返回后還要在其他地方繼續(xù)使用,在這種情況下,為了保證閉包函數(shù)的正常運行,無論閉包函數(shù)在哪里,i 都不能回收,所以 Go 編譯器會智能地將其分配在堆上。

而后面的其他示例,都只是涉及了閉包的特性,并不是直接把閉包函數(shù)返回,因此完全可以將其分配在棧上,非常的合理。

到此這篇關于Go 中閉包的底層原理的文章就介紹到這了,更多相關Go 閉包底層原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Go中使用gjson來操作JSON數(shù)據(jù)的實現(xiàn)

    Go中使用gjson來操作JSON數(shù)據(jù)的實現(xiàn)

    本文主要介紹了Go中使用gjson來操作JSON數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • Golang 實現(xiàn)Thrift客戶端連接池方式

    Golang 實現(xiàn)Thrift客戶端連接池方式

    這篇文章主要介紹了Golang 實現(xiàn)Thrift客戶端連接池方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言map實現(xiàn)順序讀取

    Go語言map實現(xiàn)順序讀取

    當我們遍歷 map 時,那就是輸出的鍵值對順序是不確定的,本文主要介紹了Go語言map實現(xiàn)順序讀取, 文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-05-05
  • Golang中自定義json序列化時間格式的示例代碼

    Golang中自定義json序列化時間格式的示例代碼

    Go語言作為一個由Google開發(fā),號稱互聯(lián)網的C語言的語言,自然也對JSON格式支持很好,下面這篇文章主要介紹了關于Golang中自定義json序列化時間格式的相關內容,下面話不多說了,來一起看看詳細的介紹吧
    2024-08-08
  • Go ORM的封裝解決方式詳解

    Go ORM的封裝解決方式詳解

    這篇文章主要為大家介紹了Go ORM的封裝解決方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Go使用SM3哈希算法和隨機鹽實現(xiàn)用戶密碼加密

    Go使用SM3哈希算法和隨機鹽實現(xiàn)用戶密碼加密

    在現(xiàn)代 Web 系統(tǒng)中,用戶密碼安全存儲是非常關鍵的一環(huán),本文將介紹如何使用 SM3 哈希算法 + 隨機鹽 (salt) 來加密用戶密碼,并提供 Go 語言實現(xiàn)示例,需要的朋友可以參考下
    2025-12-12
  • 基于GORM實現(xiàn)CreateOrUpdate方法詳解

    基于GORM實現(xiàn)CreateOrUpdate方法詳解

    這篇文章主要為大家介紹了基于GORM實現(xiàn)CreateOrUpdate方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • Go 驗證字符串中是否包含中文(推薦)

    Go 驗證字符串中是否包含中文(推薦)

    這篇文章主要介紹了Go 驗證字符串中是否包含中文,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-01-01
  • 深入探討Go語言中的map是否是并發(fā)安全以及解決方法

    深入探討Go語言中的map是否是并發(fā)安全以及解決方法

    這篇文章主要來和大家探討?Go?語言中的?map?是否是并發(fā)安全的,并提供三種方案來解決并發(fā)問題,文中的示例代碼講解詳細,需要的可以參考一下
    2023-05-05
  • 解決golang.org不能訪問的問題(推薦)

    解決golang.org不能訪問的問題(推薦)

    這篇文章主要介紹了解決golang.org不能訪問的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-11-11

最新評論

长乐市| 当阳市| 阳春市| 大新县| 藁城市| 霞浦县| 健康| 定西市| 寻乌县| 赞皇县| 荣昌县| 黄陵县| 刚察县| 建德市| 紫阳县| 吉林省| 阿克陶县| 新兴县| 贵德县| 达州市| 包头市| 铜陵市| 织金县| 广灵县| 松滋市| 蒲江县| 岢岚县| 象州县| 尚义县| 南昌县| 六安市| 临澧县| 瑞金市| 南京市| 宁强县| 东港市| 容城县| 津南区| 探索| 游戏| 盐津县|