通過匯編看golang函數(shù)的多返回值

golang這門語言,有個比較好的特性,就是支持函數(shù)的多返回值粗梭。想C柿究,C++,Java等這些語言,是不支持函數(shù)多返回的。但是C,C++可以使用傳遞指針寨腔,實現(xiàn)函數(shù)多返回。但是率寡,你有沒有想過迫卢,golang是怎樣實現(xiàn)函數(shù)多返回值的呢?
我們知道冶共,C乾蛤,C++是通過寄存器實現(xiàn)函數(shù)返回值的,也就是先把返回值寫入到一個寄存器中捅僵,然后再從寄存器中家卖,讀到函數(shù)的返回值。golang也是這樣實現(xiàn)的嗎庙楚?

偉大的思想家孔子曾說過上荡,在源碼面前一切都如同裸奔。后來馒闷,魯迅先生酪捡,總結(jié)了孔子的思想叁征,說出了,在匯編面前沛善,一切語法都是紙老虎航揉。

下面我們通過golang的匯編指令塞祈,來看一下golang是怎樣實現(xiàn)函數(shù)的多返回值的

在看匯編之前金刁,我們先用go的debug函數(shù)看下函數(shù)的棧信息
代碼很簡單,不用解釋了

package main

import (
    "fmt"
    "runtime/debug"
)

func main() {
    one(3)
}

func one(a int) (int, int) {
    fmt.Println(string(debug.Stack()))
    return a, a + 5
}


我標(biāo)紅的這一行议薪,就是one 函數(shù)的棧信息尤蛮,第一個參數(shù) 0x3 很好理解,就是我們傳入的參數(shù)3斯议, 但是后面這兩個是啥产捞?還有,我明明只傳了一個參數(shù)哼御,為啥會傳入三個參數(shù)坯临?

到這里,我也就不賣關(guān)子了恋昼,直接說了看靠,后面這兩個參數(shù),就是one函數(shù)返回值的地址液肌,也就是說挟炬,one函數(shù)返回值地址不在one函數(shù)中,而是在調(diào)用one函數(shù)的mian函數(shù)中嗦哆。golang的函數(shù)返回值谤祖,和C,C++的不同老速,golang的返回值是通過棧內(nèi)地址實現(xiàn)的(返回值的地址是由函數(shù)調(diào)用者提供)粥喜。

package main

func main() {
    var b, c *int
    one(3, b, c)
}

func one(a int, b, c *int) {
}

也就是說,剛開始的那段代碼橘券,和這段在功能實現(xiàn)上额湘,沒有什么差別,只是golang編譯器提供的一個語法糖约郁。

下面通過匯編來看一下
這次我們不是對深入分析golang的匯編缩挑,只是從匯編層面,驗證我們之前結(jié)論(golang函數(shù)多返回問題)
所以鬓梅,不會死磕plan9匯編語法供置,說實話,plan9的很多知識我也不懂绽快,大學(xué)沒開過匯編的課程芥丧,這些東西都是因為興趣自學(xué)的紧阔。

golang用的是plan9匯編,看plan9之前续担,先了解一下plan9的幾個概念

go匯編中有4個偽寄存器

  • FP: Frame pointer擅耽,指向棧底位置,一般用來引用函數(shù)的輸入?yún)?shù)物遇,用來訪問函數(shù)的參數(shù)
  • PC: Program counter: 程序計數(shù)器乖仇,用于分支和跳轉(zhuǎn)
  • SB: Static base pointer: 一般用于聲明函數(shù)或者全局變量
  • SP: Stack pointer:指向當(dāng)前棧幀的局部變量的開始位置(棧頂位置),一般用來引用函數(shù)的局部變量

我們用這段代碼進(jìn)行匯編

package main

func main() {
    one(3)
}

func one(a int) (int, int) {
    return a, a + 5
}

使用 go tool compile -N -l -S main.go 得到匯編代碼

"".main STEXT nosplit size=2 args=0x0 locals=0x0
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:3)      TEXT    "".main(SB), NOSPLIT|ABIInternal, $0-0
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:3)      FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:3)      FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:3)      FUNCDATA        $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:4)      PCDATA  $2, $0
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:4)      PCDATA  $0, $0
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:4)      XCHGL   AX, AX
        0x0001 00001 (<unknown line number>)    RET
        0x0000 90 c3                                            ..
"".one STEXT nosplit size=20 args=0x18 locals=0x0
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:7)      TEXT    "".one(SB), NOSPLIT|ABIInternal, $0-24
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:7)      FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:7)      FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:7)      FUNCDATA        $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:8)      PCDATA  $2, $0
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:8)      PCDATA  $0, $0
        0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:8)      MOVQ    "".a+8(SP), AX
        0x0005 00005 (C:\Users\bruce\Desktop\go\main.go:8)      MOVQ    AX, "".~r1+16(SP)
        0x000a 00010 (C:\Users\bruce\Desktop\go\main.go:8)      ADDQ    $5, AX
        0x000e 00014 (C:\Users\bruce\Desktop\go\main.go:8)      MOVQ    AX, "".~r2+24(SP)
        0x0013 00019 (C:\Users\bruce\Desktop\go\main.go:8)      RET
        0x0000 48 8b 44 24 08 48 89 44 24 10 48 83 c0 05 48 89  H.D$.H.D$.H...H.
        0x0010 44 24 18 c3                                      D$..

我只截取了和main询兴,one函數(shù)相關(guān)的部分
TEXT "".one(SB), NOSPLIT|ABIInternal, $0-24這行最后乃沙, $0-24 的含義,0代表one函數(shù)的棧幀大小(局部變量+可能需要的額外調(diào)用函數(shù)的參數(shù)空間的總大小)诗舰,因為one函數(shù)中沒有額外開銷警儒,所有大小是0,24是傳入?yún)?shù)和返回值的大小眶根,單位是字節(jié)蜀铲。傳入的參數(shù)和返回值都是int,在64位機(jī)器上属百,大小是8個字節(jié)记劝,64位。


簡單畫一下棧的示意圖

看一下這句0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:8) MOVQ "".a+8(SP), AX
SP寄存器指向的是棧頂?shù)奈恢茫?strong>AX 是一個通用寄存器
MOVQ指令 把 參數(shù)a 也就是(SP+8)的值搬到AX中


0x0005 00005 (C:\Users\bruce\Desktop\go\main.go:8) MOVQ AX, "".~r1+16(SP)
同樣诸老,把AX中的值搬到r1(返回值b)


0x000a 00010 (C:\Users\bruce\Desktop\go\main.go:8) ADDQ $5, AX
ADDQ指令把AX值+5

0x000e 00014 (C:\Users\bruce\Desktop\go\main.go:8) MOVQ AX, "".~r2+24(SP)
最后把AX的值搬到r2(返回值c)

0x0013 00019 (C:\Users\bruce\Desktop\go\main.go:8) RET
最后RET指令隆夯,one函數(shù)結(jié)束

總結(jié)

通過對golang進(jìn)行匯編,真實了之前的結(jié)論

golang函數(shù)的多返回值不是通過寄存器傳遞别伏,使用過使用調(diào)用值提供的地址蹄衷,賦值實現(xiàn)的

先寫這些吧,我也是剛接觸golang的匯編厘肮,文中如有不正確的地方愧口,還請在評論區(qū)指出

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市类茂,隨后出現(xiàn)的幾起案子耍属,更是在濱河造成了極大的恐慌,老刑警劉巖巩检,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厚骗,死亡現(xiàn)場離奇詭異,居然都是意外死亡兢哭,警方通過查閱死者的電腦和手機(jī)领舰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冲秽,你說我怎么就攤上這事舍咖。” “怎么了锉桑?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵排霉,是天一觀的道長。 經(jīng)常有香客問我民轴,道長攻柠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任杉武,我火速辦了婚禮辙诞,結(jié)果婚禮上辙售,老公的妹妹穿的比我還像新娘轻抱。我一直安慰自己,他們只是感情好旦部,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布祈搜。 她就那樣靜靜地躺著,像睡著了一般士八。 火紅的嫁衣襯著肌膚如雪容燕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天婚度,我揣著相機(jī)與錄音蘸秘,去河邊找鬼。 笑死蝗茁,一個胖子當(dāng)著我的面吹牛醋虏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哮翘,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼颈嚼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饭寺?” 一聲冷哼從身側(cè)響起阻课,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎艰匙,沒想到半個月后限煞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡员凝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年署驻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡硕舆,死狀恐怖秽荞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抚官,我是刑警寧澤扬跋,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站凌节,受9級特大地震影響钦听,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜倍奢,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一朴上、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卒煞,春花似錦痪宰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扮饶,卻和暖如春具练,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甜无。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工扛点, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岂丘。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓陵究,卻偏偏與公主長得像,于是被迫代替她去往敵國和親元潘。 傳聞我的和親對象是個殘疾皇子畔乙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345