婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 深入分析golang多值返回以及閉包的實現(xiàn)

深入分析golang多值返回以及閉包的實現(xiàn)

熱門標(biāo)簽:最短的地圖標(biāo)注 ?兓? 浙江人工智能外呼管理系統(tǒng) 電銷機(jī)器人可以補(bǔ)救房產(chǎn)中介嗎 成都呼叫中心外呼系統(tǒng)平臺 電梯外呼訪客系統(tǒng) 騰訊外呼系統(tǒng)價格 百度地圖標(biāo)注搜索關(guān)鍵詞 谷歌便利店地圖標(biāo)注

一、前言

golang有很多新穎的特性,不知道大家的使用的時候,有沒想過,這些特性是如何實現(xiàn)的?當(dāng)然你可能會說,不了解這些特性好像也不影響自己使用golang,你說的也有道理,但是,多了解底層的實現(xiàn)原理,對于在使用golang時的眼界是完全不一樣的,就類似于看過http的實現(xiàn)之后,再來使用http框架,和未看過http框架時的眼界是不一樣的,當(dāng)然,你如果是一名it愛好者,求知欲自然會引導(dǎo)你去學(xué)習(xí)。

二、這篇文章主要就分析兩點(diǎn):

     1、golang多值返回的實現(xiàn);

     2、golang閉包的實現(xiàn);

三、golang多值返回的實現(xiàn)

我們在學(xué)C/C++時,很多人應(yīng)該有了解過C/C++函數(shù)調(diào)用過程,參數(shù)是通過寄存器di和si(假設(shè)就兩個參數(shù))傳遞給被調(diào)用的函數(shù),被調(diào)用函數(shù)的返回結(jié)果只能是通過eax寄存器返回給調(diào)用函數(shù),因此C/C++函數(shù)只能返回一個值,那么我們是不是可以想象,golang的多值返回是否可以通過多個寄存器來實現(xiàn)的,正如用多個寄存器來傳參一樣?

這也是一種辦法,但是golang并沒有采用;我的理解是引入多個寄存器來存儲返回值,會引起多個寄存器用途的重新約定,這無疑增加了復(fù)雜度;可以這么說,golang的ABI與C/C++非常不一樣;

在從匯編角度分析golang多值返回之前,需要先熟悉golang匯編代碼的一些約定, golang官網(wǎng) 有說明,這里重點(diǎn)說明四個symbols,需要注意的是這里的寄存器是偽寄存器:

       1.FP 棧底寄存器,指向一個函數(shù)棧的頂部;

       2.PC 程序計數(shù)器,指向下一條執(zhí)行指令;

       3.SB 指向靜態(tài)數(shù)據(jù)的基指針,全局符號;

       4.SP 棧頂寄存器;

這里面最重要的就是FP和SP,F(xiàn)P寄存器主要用于取參數(shù)以及存返回值,golang函數(shù)調(diào)用的實現(xiàn)很大程度上都是依賴這兩個寄存器,這里先給出結(jié)果,

+-----------+---\

| 返回值2 | \

+-----------+  \

| 返回值1 |  \

+---------+-+  
| 參數(shù)2 |  這些在調(diào)用函數(shù)中
+-----------+  
| 參數(shù)1 |   /
+-----------+  /
| 返回地址 | /
+-----------+--\/-----fp值
| 局部變量 | \

| ... | 被調(diào)用數(shù)棧禎
|   | /
+-----------+--/+---sp值

這個就是golang的一個函數(shù)棧,也是說函數(shù)傳參是通過fp+offset來實現(xiàn)的,而多個返回值也是通過fp+offset存儲在調(diào)用函數(shù)的棧幀中。

下面通過一個例子來分析

package main

import "fmt"

func test(i, j int) (int, int) {
a:=i+ j
b:=i- j
 return a,b
}

func main() {
a,b:= test(2,1)
 fmt.Println(a, b)
}

這個例子很簡單,主要是為了說明golang多值返回的過程;我們通過下面命令編譯該程序

go tool compile -S test.go > test.s

然后,就可以打開test.s,來看下這個小程序的匯編代碼。首先來看下test函數(shù)的匯編代碼

"".test t=1size=32value=0args=0x20locals=0x0
0x000000000(test.go:5) TEXT"".test(SB),$0-32//棧大小為32字節(jié)
0x000000000(test.go:5)NOP
0x000000000(test.go:5)NOP
0x000000000(test.go:5)MOVQ"".i+8(FP),CX//取第一個參數(shù)i
0x000500005(test.go:5)MOVQ"".j+16(FP),AX//取第二個參數(shù)j
0x000a00010(test.go:5) FUNCDATA$0, gclocals·a8eabfc4a4514ed6b3b0c61e9680e440(SB)
0x000a00010(test.go:5) FUNCDATA$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x000a00010(test.go:6)MOVQCX,BX//將i放入bx
0x000d00013(test.go:6) ADDQAX,CX//i+j放入cx
0x001000016(test.go:7) SUBQAX,BX//i-j放入bx
 //將返回結(jié)果存入調(diào)用函數(shù)棧幀
0x001300019(test.go:8)MOVQCX,"".~r2+24(FP)
 //將返回結(jié)果存入調(diào)用函數(shù)棧幀
0x001800024(test.go:8)MOVQBX,"".~r3+32(FP)
0x001d00029(test.go:8)RET

由這個匯編代碼可以看出來,在test函數(shù)內(nèi)部,是通過fp+8取第一個參數(shù),fp+16取第二個參數(shù);然后將返回的第一個值存入fp+24,返回的第二個值存入fp+32,和我上述所說完全一致;golang函數(shù)調(diào)用過程,是通過fp+offset來實現(xiàn)傳參和返回值,而不像C/C++都是通過寄存器實現(xiàn)傳參和返回值;

但是,這里有個問題,我的變量都是int類型,為啥分配的都是8字節(jié),這有待考證。

本來想通過查看main函數(shù)的棧幀來驗證之前的結(jié)論,但是golang對小函數(shù)自動轉(zhuǎn)為內(nèi)聯(lián)函數(shù),因此你們可以自己編譯出來看看,main函數(shù)內(nèi)部是沒有調(diào)用test函數(shù)的,而是將test函數(shù)的匯編代碼直接拷貝進(jìn)main函數(shù)執(zhí)行了。

四、golang閉包的實現(xiàn)

之前有去看了下C++11的lambda函數(shù)的實現(xiàn),其實實現(xiàn)原理就是仿函數(shù);編譯器在編譯lambda函數(shù)時,會生成一個匿名的仿函數(shù)類,然后執(zhí)行這個lambda函數(shù)時,會調(diào)用編譯生成的匿名仿函數(shù)類重載函數(shù)調(diào)用方法,這個方法也就是lambda函數(shù)中定義的方法;其實golang閉包的實現(xiàn)和這個類似,我們通過例子來說明

packagemain

import"fmt"

functest(aint)func(iint)int{
returnfunc(iint)int{
 a = a + i
returna
 }
}

funcmain(){
 f := test(1)
 a := f(2)
 fmt.Println(a)
 b := f(3)
 fmt.Println(b)
}

這個例子程序很簡單,test函數(shù)傳入一個整型參數(shù)a,返回一個函數(shù)類型;這個函數(shù)類型傳入一個整型參數(shù)以及返回一個整型值;main函數(shù)調(diào)用test函數(shù),返回一個閉包函數(shù)。

來看下test函數(shù)的匯編代碼:

"".test t=1size=160value=0args=0x10locals=0x20
0x000000000(test.go:5) TEXT"".test(SB),$32-16
0x000000000(test.go:5)MOVQ(TLS),CX
0x000900009(test.go:5) CMPQSP,16(CX)
0x000d00013(test.go:5) JLS142
0x000f00015(test.go:5) SUBQ$32,SP
0x001300019(test.go:5) FUNCDATA$0, gclocals·8edb5632446ada37b0a930d010725cc5(SB)
0x001300019(test.go:5) FUNCDATA$1, gclocals·008e235a1392cc90d1ed9ad2f7e76d87(SB)
0x001300019(test.go:5) LEAQ type.int(SB),BX
0x001a00026(test.go:5)MOVQBX, (SP)
0x001e00030(test.go:5) PCDATA$0,$0
 //生成一個int型對象,即a
0x001e00030(test.go:5)CALLruntime.newobject(SB)
 //8(sp)即生成的a的地址,放入AX
0x002300035(test.go:5)MOVQ8(SP),AX
 //將a的地址存入sp+24的位置
0x002800040(test.go:5)MOVQAX,"".a+24(SP)
 //取出main函數(shù)傳入的第一個參數(shù),即a
0x002d00045(test.go:5)MOVQ"".a+40(FP),BP
 //將a放入(AX)指向的內(nèi)存,即上述新生成的int型對象
0x003200050(test.go:5)MOVQBP, (AX)
0x003500053(test.go:6) LEAQ type.struct { F uintptr; a *int }(SB), BX
0x003c00060(test.go:6)MOVQBX, (SP)
0x004000064(test.go:6) PCDATA$0,$1
0x004000064(test.go:6)CALLruntime.newobject(SB)
 //8(sp)這就是上述生成的struct對象地址
0x004500069(test.go:6)MOVQ8(SP),AX
0x004a00074(test.go:6)NOP
 //test內(nèi)部匿名函數(shù)地址存入BP
0x004a00074(test.go:6) LEAQ"".test.func1(SB),BP
 //將匿名函數(shù)地址放入(AX)指向的地址,即給上述
 //F uintptr賦值
0x005100081(test.go:6)MOVQBP, (AX)
0x005400084(test.go:6)MOVQAX,"".autotmp_0001+16(SP)
0x005900089(test.go:6)NOP
 //將上述生成的整型對象a的地址存入BP
0x005900089(test.go:6)MOVQ"".a+24(SP),BP
0x005e00094(test.go:6) CMPB runtime.writeBarrier(SB),$0
0x006500101(test.go:6)JNE$0,117
 //將a地址存入AX指向內(nèi)存+8,
 //即為上述結(jié)構(gòu)體a *int賦值
0x006700103(test.go:6)MOVQBP,8(AX)
 //將上述結(jié)構(gòu)體的地址存入main函數(shù)棧幀中;
0x006b00107(test.go:9)MOVQAX,"".~r1+48(FP)
0x007000112(test.go:9) ADDQ$32,SP
0x007400116(test.go:9)RET

之前有看到一句話,很形象地描述了閉包

類是有行為的數(shù)據(jù),為閉包是有數(shù)據(jù)的行為;

也就是說閉包是有上下文的,我們以測試?yán)訛槔ㄟ^test函數(shù)生成的閉包函數(shù),都有各自的a,這個a就是閉包的上下文數(shù)據(jù),而且這個a一直伴隨著他的閉包函數(shù),每調(diào)用一次,a都會發(fā)生變化;

我們分析了上述匯編代碼,來看下閉包實現(xiàn)原理;在這個測試?yán)又校捎?code>a是閉包的上下文數(shù)據(jù),因此a必須在堆上分配,如果在棧上分配,函數(shù)結(jié)束,a也被回收了;然后會定義出一個匿名結(jié)構(gòu)體:

type.struct{
 F uintptr//這個就是閉包調(diào)用的函數(shù)指針
 a *int//這就是閉包的上下文數(shù)據(jù)
}

接著生成一個該對象,并將之前在堆上分配的整型對象a的地址賦值給結(jié)構(gòu)體中的a指針,接下來將閉包調(diào)用的func函數(shù)地址賦值給結(jié)構(gòu)體中F指針;這樣,每生成一個閉包函數(shù),其實就是生成一個上述結(jié)構(gòu)體對象,每個閉包對象也就有自己的數(shù)據(jù)a和調(diào)用函數(shù)F;最后將這個結(jié)構(gòu)體的地址返回給main函數(shù);

來看下main函數(shù)獲取閉包的過程;

"".main t=1size=528value=0args=0x0locals=0x88
0x000000000(test.go:12) TEXT"".main(SB),$136-0
0x000000000(test.go:12)MOVQ(TLS),CX
0x000900009(test.go:12) LEAQ -8(SP),AX
0x000e00014(test.go:12) CMPQAX,16(CX)
0x001200018(test.go:12) JLS506
0x001800024(test.go:12) SUBQ$136,SP
0x001f00031(test.go:12) FUNCDATA$0, gclocals·f5be5308b59e045b7c5b33ee8908cfb7(SB)
0x001f00031(test.go:12) FUNCDATA$1, gclocals·9d868b227cedd8dd4b1bec8682560fff(SB)
 //將參數(shù)1(f:=test(1))放入main函數(shù)棧頂
0x001f00031(test.go:13)MOVQ$1, (SP)
0x002700039(test.go:13) PCDATA$0,$0
 //調(diào)用main函數(shù)生成閉包對象
0x002700039(test.go:13)CALL"".test(SB)
 //將閉包對象的地址放入DX
0x002c00044(test.go:13)MOVQ8(SP),DX
 //將參數(shù)2(a:=f(2))放入棧頂
0x003100049(test.go:14)MOVQ$2, (SP)
0x003900057(test.go:14)MOVQDX,"".f+56(SP)
 //將閉包對象的函數(shù)指針賦值給BX
0x003e00062(test.go:14)MOVQ(DX),BX
0x004100065(test.go:14) PCDATA$0,$1
 //這里調(diào)用閉包函數(shù),并且將閉包對象的地址也傳進(jìn)
 //閉包函數(shù),為了修改a嘛
0x004100065(test.go:14)CALLDX,BX
0x004300067(test.go:14)MOVQ8(SP),BX

很明顯,main函數(shù)調(diào)用test函數(shù)獲取的是閉包對象的地址,通過這個閉包對象地址找到閉包函數(shù),然后執(zhí)行這個閉包函數(shù),并且把閉包對象的地址傳進(jìn)函數(shù),這點(diǎn)和C++傳this指針原理一樣,為了修改成員變量a

最后看下test內(nèi)部的匿名函數(shù)(閉包函數(shù)實現(xiàn)):

"".test.func1t=1size=32value=0args=0x10 locals=0x0
0x000000000(test.go:6) TEXT"".test.func1(SB), $0-16
0x000000000(test.go:6) NOP
0x000000000(test.go:6) NOP
0x000000000(test.go:6) FUNCDATA $0, gclocals·23e8278e2b69a3a75fa59b23c49ed6ad(SB)
0x000000000(test.go:6) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
//DX是閉包對象的地址,+8即a的地址
0x000000000(test.go:6) MOVQ8(DX), AX
//AX為a的地址,(AX)即為a的值
0x000400004(test.go:7) MOVQ (AX), BP
//將參數(shù)i存入R8
0x000700007(test.go:7) MOVQ"".i+8(FP), R8
//a+i的值存入BP
0x000c00012(test.go:7) ADDQ R8, BP
//將a+i存入a的地址
0x000f00015(test.go:7) MOVQ BP, (AX)
//將a地址最新數(shù)據(jù)存入BP
0x001200018(test.go:8) MOVQ (AX), BP
//將a最新值作為返回值放入main函數(shù)棧中
0x001500021(test.go:8) MOVQ BP,"".~r1+16(FP)
0x001a00026(test.go:8) RET

閉包函數(shù)的調(diào)用過程:

      1、通過閉包對象地址獲取閉包上下文數(shù)據(jù)a的地址;

      2、接著通過a的地址獲取到a的值,并與參數(shù)i相加;

      3、將a+i作為最新值存入a的地址;

      4、將a最新值返回給main函數(shù);

五、總結(jié)

這篇文章簡單地從匯編角度分析了golang多值返回和閉包的實現(xiàn);

      多值返回主要是通過fp寄存器+offset獲取參數(shù)以及存入返回值實現(xiàn);

      閉包主要是通過在編譯時生成包含閉包函數(shù)和閉包上下文數(shù)據(jù)的結(jié)構(gòu)體實現(xiàn);

以上就是這篇文章的全部內(nèi)容,希望對大家學(xué)習(xí)或只用golang能有一定的幫助,如果有疑問大家可以留言交流。

您可能感興趣的文章:
  • 簡單了解Go語言中函數(shù)作為值以及函數(shù)閉包的使用
  • 舉例講解Go語言中函數(shù)的閉包使用
  • JavaScript.The.Good.Parts閱讀筆記(二)作用域閉包減緩全局空間污染
  • 深入理解Go語言中的閉包

標(biāo)簽:眉山 盤錦 七臺河 上海 邢臺 雅安 紹興 宜昌

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《深入分析golang多值返回以及閉包的實現(xiàn)》,本文關(guān)鍵詞  深入分析,golang,多值,返回,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《深入分析golang多值返回以及閉包的實現(xiàn)》相關(guān)的同類信息!
  • 本頁收集關(guān)于深入分析golang多值返回以及閉包的實現(xiàn)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    一区免费观看视频| 777欧美精品| 国产精品资源网| 美女www一区二区| 美女网站一区二区| 久久精品国产亚洲高清剧情介绍| 亚洲成在人线在线播放| 亚洲成人精品影院| 亚洲成av人片在线观看无码| 亚洲最大成人网4388xx| 亚洲国产一区二区三区| 午夜激情久久久| 蜜臀久久99精品久久久久久9| 日韩高清一级片| 欧美色爱综合网| 欧美在线一区二区| 在线不卡中文字幕| 久久久一区二区| 亚洲欧美一区二区久久| 亚洲影视资源网| 奇米888四色在线精品| 久久9热精品视频| 国产99久久精品| 色一情一乱一乱一91av| 91精品国产品国语在线不卡| 久久综合色综合88| 亚洲免费色视频| 青青草原综合久久大伊人精品| 国产自产2019最新不卡| av在线综合网| 日韩一区二区三区免费观看| 久久精品一区二区三区不卡牛牛| 亚洲人123区| 久久国产人妖系列| 成人亚洲一区二区一| 欧美日韩视频在线第一区| 久久精品视频一区| 亚洲电影中文字幕在线观看| 国产精品综合视频| 欧美日韩国产系列| 国产精品视频麻豆| 日本中文字幕不卡| 99久久精品国产毛片| 精品三级在线看| 亚洲动漫第一页| 成人性生交大合| 欧美成人激情免费网| 一区二区三区国产| 国产久卡久卡久卡久卡视频精品| 日本韩国欧美国产| 亚洲欧美自拍偷拍色图| 国产精品99久久久久| 在线成人av网站| 一区二区激情视频| 97精品久久久久中文字幕| 久久婷婷国产综合精品青草| 日本成人在线不卡视频| 欧美无砖砖区免费| 一区二区三区四区蜜桃| 99麻豆久久久国产精品免费| 久久精品网站免费观看| 免费人成在线不卡| 欧美群妇大交群中文字幕| 亚洲免费观看视频| 99精品视频在线观看免费| 欧美激情综合在线| 国产高清在线观看免费不卡| 久久久91精品国产一区二区三区| 国内精品写真在线观看| 2022国产精品视频| 国产精一区二区三区| 国产女主播一区| 成人免费毛片高清视频| 国产精品的网站| 91免费看视频| 亚洲国产欧美日韩另类综合 | 三级欧美在线一区| 欧美日韩精品电影| 亚洲最大成人网4388xx| 欧美久久久一区| 麻豆免费精品视频| 久久久国产午夜精品| 高清不卡一区二区| 国产精品成人一区二区艾草| 色欧美片视频在线观看| 亚洲一区二区三区四区五区黄| 欧美日韩一区二区电影| 免费看日韩a级影片| 欧美tk—视频vk| 丁香网亚洲国际| 亚洲欧美日韩久久精品| 欧美色图在线观看| 麻豆成人久久精品二区三区红| 久久女同性恋中文字幕| 成人动漫一区二区三区| 亚洲韩国精品一区| 精品噜噜噜噜久久久久久久久试看| 国产一区二区三区免费| 日韩美女视频一区| 日韩欧美一级片| 成人av综合在线| 亚洲电影视频在线| 久久精品亚洲乱码伦伦中文| 色乱码一区二区三区88| 久久国产剧场电影| ...中文天堂在线一区| 在线不卡免费欧美| 成人国产精品免费| 蜜桃av噜噜一区| 亚洲精品视频免费观看| 久久先锋影音av鲁色资源网| 97久久超碰精品国产| 久久99精品国产.久久久久久| 中文字幕日本不卡| 精品国产乱码久久久久久久久| av中文字幕不卡| 国产剧情在线观看一区二区| 午夜国产精品影院在线观看| 国产精品青草综合久久久久99| 欧美丰满嫩嫩电影| 99久精品国产| 国产成人日日夜夜| 美国av一区二区| 亚洲国产精品久久久久婷婷884 | 日韩午夜在线观看| 91免费版在线| 国产一区999| 免费成人你懂的| 无码av免费一区二区三区试看| 亚洲日本乱码在线观看| 久久久国产午夜精品| 日韩欧美一区二区免费| 欧美美女一区二区三区| 91国模大尺度私拍在线视频| 国产.欧美.日韩| 韩国精品免费视频| 精品制服美女丁香| 久久精品免费观看| 六月丁香综合在线视频| 日本不卡中文字幕| 日本伊人午夜精品| 青青草成人在线观看| 亚欧色一区w666天堂| 亚洲第一久久影院| 日本中文字幕一区二区有限公司| 午夜精品久久久| 爽好久久久欧美精品| 日韩中文字幕av电影| 青草国产精品久久久久久| 日韩激情一区二区| 免费日本视频一区| 韩国一区二区三区| 国产91丝袜在线播放0| 成人小视频免费观看| 成人免费看黄yyy456| 91网站最新地址| 色激情天天射综合网| 欧美日韩国产一级二级| 欧美一区二区三区视频在线 | 欧美体内she精高潮| 欧美日韩一级大片网址| 欧美电影一区二区三区| 日韩欧美国产系列| 中文字幕精品一区二区精品绿巨人 | 亚洲精品久久7777| 亚洲成人一二三| 久久99国产精品成人| 丰满放荡岳乱妇91ww| 91黄色小视频| 日韩欧美中文字幕精品| 国产日韩欧美激情| 一个色在线综合| 麻豆中文一区二区| 成人激情免费网站| 欧美疯狂性受xxxxx喷水图片| 欧美mv日韩mv国产网站| 日韩美女视频19| 久久99热99| 91福利国产成人精品照片| 日韩一区二区三| 亚洲欧美另类久久久精品2019| 天天影视网天天综合色在线播放| 国产酒店精品激情| 欧美亚洲动漫精品| 中文字幕精品一区| 日韩电影在线一区二区三区| 成人美女视频在线观看18| 欧美高清一级片在线| 国产精品传媒视频| 九一九一国产精品| 91高清视频在线| 日本一区二区三区免费乱视频 | 亚洲久草在线视频| 国产自产v一区二区三区c| 欧美日韩精品福利| 日韩毛片高清在线播放| 国产成人免费在线视频| 4438x亚洲最大成人网| 一级中文字幕一区二区| 成人免费精品视频|