时间:2021-05-19
golang这门语言,有个比较好的特性,就是支持函数的多返回值。想C,C++,Java等这些语言,是不支持函数多返回的。但是C,C++可以使用传递指针,实现函数多返回。但是,你有没有想过,golang是怎样实现函数多返回值的呢?
我们知道,C,C++是通过寄存器实现函数返回值的,也就是先把返回值写入到一个寄存器中,然后再从寄存器中,读到函数的返回值。golang也是这样实现的吗?
伟大的思想家孔子曾说过,在源码面前一切都如同裸奔。后来,鲁迅先生,总结了孔子的思想,说出了,在汇编面前,一切语法都是纸老虎。
下面我们通过golang的汇编指令,来看一下golang是怎样实现函数的多返回值的
在看汇编之前,我们先用go的debug函数看下函数的栈信息
代码很简单,不用解释了
package mainimport ( "fmt" "runtime/debug")func main() { one(3)}func one(a int) (int, int) { fmt.Println(string(debug.Stack())) return a, a + 5}我标红的这一行,就是one函数的栈信息,第一个参数0x3很好理解,就是我们传入的参数3
, 但是后面这两个是啥?还有,我明明只传了一个参数,为啥会传入三个参数?
到这里,我也就不卖关子了,直接说了,后面这两个参数,就是one函数返回值的地址,也就是说,one函数返回值地址不在one函数中,而是在调用one函数的mian函数中。golang的函数返回值,和C,C++的不同,golang的返回值是通过栈内地址实现的(返回值的地址是由函数调用者提供)。
package mainfunc main() { var b, c *int one(3, b, c)}func one(a int, b, c *int) {}也就是说,刚开始的那段代码,和这段在功能实现上,没有什么差别,只是golang编译器提供的一个语法糖。
下面通过汇编来看一下
这次我们不是对深入分析golang的汇编,只是从汇编层面,验证我们之前结论(golang函数多返回问题)
所以,不会死磕plan9汇编语法,说实话,plan9的很多知识我也不懂,大学没开过汇编的课程,这些东西都是因为兴趣自学的。
golang用的是plan9汇编,看plan9之前,先了解一下plan9的几个概念
go汇编中有4个伪寄存器
我们用这段代码进行汇编
package mainfunc 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函数相关的部分
TEXT "".one(SB), NOSPLIT|ABIInternal, $0-24 这行最后, $0-24的含义,0代表one函数的栈帧大小(局部变量+可能需要的额外调用函数的参数空间的总大小),因为one函数中没有额外开销,所有大小是0,24是传入参数和返回值的大小,单位是字节。传入的参数和返回值都是int,在64位机器上,大小是8个字节,64位。
简单画一下栈的示意图
看一下这句 0x0000 00000 (C:\Users\bruce\Desktop\go\main.go:8) MOVQ "".a+8(SP), AX
SP寄存器指向的是栈顶的位置,AX是一个通用寄存器
MOVQ指令 把 参数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函数结束
总结
通过对golang进行汇编,真实了之前的结论
golang函数的多返回值不是通过寄存器传递,使用过使用调用值提供的地址,赋值实现的
先写这些吧,我也是刚接触golang的汇编,文中如有不正确的地方,还请指出
到此这篇关于通过汇编看golang函数的多返回值的文章就介绍到这了,更多相关汇编golang函数多返回值内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
1.Lua函数支持多返回值,但并不是每次调用函数返回的全部值都会被使用。有一条规则是只有当函数调用是表达式最后一个元素时,才会使用它的全部返回值。看代码:复制代
golang函数如果返回值定义了变量,return后边必须跟着表达式或者值funcmain(){fmt.Println("-------",test())}fu
01、函数参数和返回值的作用函数根据有没有参数以及有没有返回值,可以相互结合,共有四种:无参数无返回值无参数有返回值有参数无返回值有参数有返回值定义函数时候,是
函数返回值和返回引用是不同的函数返回值时会产生一个临时变量作为函数返回值的副本,而返回引用时不会产生值的副本,既然是引用,那引用谁呢?这个问题必须清楚,否则将无
序本文主要研究一下golang的deferdeferreturn先赋值(对于命名返回值),然后执行defer,最后函数返回defer函数调用的执行顺序与它们分别