<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          函數(shù)——go世界中的一等公民

          共 3818字,需瀏覽 8分鐘

           ·

          2020-07-28 18:32

          作者:Destiny池魚(yú)
          來(lái)源:SegmentFault思否社區(qū)




          函數(shù)的本質(zhì)


          在go的世界中,函數(shù)是一等公民,可以給變量賦值,可以作為參數(shù)傳遞,也可以直接賦值。

          ? ? ? ? ? ? ? ?

          package main
          import ( "fmt" "time")
          func A() { // ... fmt.Println("this is a")}
          func B(f func()) { // ...}
          func C() func() { return A}
          var f func() = C()
          func main() { time.Sleep(time.Minute) v := C() v()}



          在go語(yǔ)言中將這樣的變量、參數(shù)、返回值,即在堆空間和棧空間中綁定函數(shù)的值,稱為function value


          函數(shù)的指令在編譯期間生成,使用go tool compile -S main.go可以獲取匯編代碼, 以O(shè)SX 10.15.6,go 1.14為例,將看到下述匯編代碼(下面只引用部分)

          ? ? ? ? ? ? ? ?

          ..."".B STEXT nosplit size=1 args=0x8 locals=0x0    0x0000 00000 (main.go:9)    TEXT    "".B(SB), NOSPLIT|ABIInternal, $0-8    0x0000 00000 (main.go:9)    PCDATA    $0, $-2    0x0000 00000 (main.go:9)    PCDATA    $1, $-2    0x0000 00000 (main.go:9)    FUNCDATA    $0, gclocals·2a5305abe05176240e61b8620e19a815(SB)    0x0000 00000 (main.go:9)    FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)    0x0000 00000 (main.go:9)    FUNCDATA    $2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)    0x0000 00000 (main.go:11)    PCDATA    $0, $-1    0x0000 00000 (main.go:11)    PCDATA    $1, $-1    0x0000 00000 (main.go:11)    RET    0x0000 c3...


          運(yùn)行時(shí)將存放在__TEXT段中,也就是存放在代碼段中,讀寫(xiě)權(quán)限為rx/rwx, 通過(guò)vmmap [pid]可以獲取運(yùn)行時(shí)的內(nèi)存分布

          ? ? ? ? ? ? ? ?

          ==== Non-writable regions for process 13443
          REGION TYPE START - END [ VSIZE RSDNT DIRTY SWAP] PRT/MAX SHRMOD PURGE REGION DETAIL
          __TEXT 0000000001000000-0000000001167000 [ 1436K 1436K 0K 0K] r-x/rwx SM=COW .../test

          使用otool -v -l [file]可以看到下述內(nèi)容(下面只引用了一部分)

          ? ? ? ? ? ? ? ?

          ...Load command 1      cmd LC_SEGMENT_64  cmdsize 632  segname __TEXT   vmaddr 0x0000000001000000   vmsize 0x0000000000167000  fileoff 0 filesize 1470464  maxprot rwx initprot r-x   nsects 7    flags (none)Section  sectname __text   segname __TEXT      addr 0x0000000001001000      size 0x000000000009c365    offset 4096     align 2^4 (16)    reloff 0    nreloc 0      type S_REGULARattributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS reserved1 0 reserved2 0...


          所以如果要問(wèn)函數(shù)在go語(yǔ)言里的本質(zhì)是什么,那么其實(shí)就是指向__TEXT段內(nèi)存地址的一個(gè)指針





          函數(shù)調(diào)用的過(guò)程


          在go語(yǔ)言中,每一個(gè)goroutine持有一個(gè)連續(xù)棧,棧基礎(chǔ)大小為2kb,當(dāng)棧大小超過(guò)預(yù)分配大小后,會(huì)觸發(fā)棧擴(kuò)容,也就是分配一個(gè)大小為當(dāng)前棧2倍的新棧,并且將原來(lái)的棧拷貝到新的棧上。使用連續(xù)棧而不是分段棧的目的是,利用局部性優(yōu)勢(shì)提升執(zhí)行速度,原理是CPU讀取地址時(shí)會(huì)將相鄰的內(nèi)存讀取到訪問(wèn)速度比內(nèi)存快的多級(jí)cache中,地址連續(xù)性越好,L1、L2、L3 cache命中率越高,速度也就越快。


          在go中,和其他一些語(yǔ)言有所不同,函數(shù)的返回值、參數(shù)都是由被caller保存。每次函數(shù)調(diào)用時(shí),會(huì)在caller的棧中壓入函數(shù)返回值列表、參數(shù)列表、函數(shù)返回時(shí)的PC地址,然后更改bp和pc為新函數(shù),執(zhí)行新函數(shù),執(zhí)行完之后將變量存到caller的棧空間中,利用棧空間中保存的返回地址和caller的棧基地址,恢復(fù)pc和sp回到caller的執(zhí)行過(guò)程。


          對(duì)于棧變量的訪問(wèn)是通過(guò)bp+offset的方式來(lái)訪問(wèn),而對(duì)于在堆上分配的變量來(lái)說(shuō),就是通過(guò)地址來(lái)訪問(wèn)。在go中,變量被分配到堆上還是被分配到棧上是由編譯器在編譯時(shí)根據(jù)逃逸分析決定的,不可以更改,只能利用規(guī)則盡量讓變量被分配到棧上,因?yàn)榫植啃詢?yōu)勢(shì),棧空間的內(nèi)存訪問(wèn)速度快于堆空間訪問(wèn)。





          方法的本質(zhì)


          go里面其實(shí)方法就是語(yǔ)法糖,請(qǐng)看下述代碼,兩個(gè)Println打印的結(jié)果是一樣的,實(shí)際上Method就是將receiver作為函數(shù)的第一個(gè)參數(shù)輸入的語(yǔ)法糖而已,本質(zhì)上和函數(shù)沒(méi)有區(qū)別

          ? ? ? ? ? ? ? ?

          type T struct {    name string}
          func (t T) Name() string { return "Hi! " + t.name}
          func main() { t := T{name: "test"} fmt.Println(t.Name()) // Hi! test fmt.Println(T.Name(t)) // Hi! test}





          閉包的本質(zhì)


          前面已經(jīng)提到在go語(yǔ)言中將這在堆空間和棧空間中綁定函數(shù)的值,稱為function value。這也就是閉包在go語(yǔ)言中的實(shí)體。一個(gè)最簡(jiǎn)單的funcval實(shí)際上是通過(guò)二級(jí)指針指向__TEXT代碼段上函數(shù)的結(jié)構(gòu)體。


          那我們來(lái)看下面這個(gè)閉包,也就是main函數(shù)中的變量f

          ? ? ? ? ? ? ? ?

          func getFunc() func() int {    a := 0    return func() int {        a++        return a    }}
          func main() { f := getFunc() for i := 0; i < 10; i++ { fmt.Println(f()) }}


          上面這段代碼執(zhí)行完后會(huì)輸出1~10,也就是說(shuō)f在執(zhí)行的時(shí)候所使用的a會(huì)累計(jì),但是a并不是一個(gè)全局變量,為什么f就變成了一個(gè)有狀態(tài)的函數(shù)呢?其實(shí)這也就是go里面的閉包了。那我們來(lái)看go是如何實(shí)現(xiàn)閉包的。


          首先來(lái)解釋一下閉包的含義,閉包在實(shí)現(xiàn)上一個(gè)結(jié)構(gòu)體,需要存儲(chǔ)函數(shù)入口和關(guān)聯(lián)環(huán)境,關(guān)聯(lián)環(huán)境包含約束變量(函數(shù)內(nèi)部變量)和自由變量(函數(shù)外部變量,在函數(shù)外被定義,但是在函數(shù)內(nèi)被引用),和函數(shù)不同的事,在捕獲閉包時(shí)才能確定自由變量,當(dāng)脫離了捕捉變量的上下文時(shí),也能照常運(yùn)行。基于閉包可以很容易的定義異步調(diào)用的回調(diào)函數(shù)。


          在 go 語(yǔ)言中,閉包的狀態(tài)是通過(guò)捕獲列表實(shí)現(xiàn)的。具體來(lái)說(shuō),有自由變量的閉包funcval的分配都在堆上,(沒(méi)有自由變量的funcval在__DATA數(shù)據(jù)段上,和常量一樣),funcval中除了包含地址以外,還會(huì)包含所引用的自由變量,所有自由變量構(gòu)成捕獲列表。對(duì)于會(huì)被修改的值,捕獲的是值的指針,對(duì)于不會(huì)被修改的值,捕獲的是值拷貝。





          點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開(kāi)更多互動(dòng)和交流。

          瀏覽 82
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美在线观看视频一区 | 91亚洲精品久久久久蜜桃 | 欧美一区二区黄色 | 豆花视频精品一区 | 91精品国久久久久久无码一区二区三区 |