深入分析golang多值返回以及闭包的实现_Golang

来源:脚本之家  责任编辑:小易  

所谓返回值,函数才有,就类似于数学中的函数的概念f(x),其实跟数学概念一开始是一个意思,你传入一个值x,经过处理,然后返回处理的结果,处理的结果就叫做返回值。就好像你拿原材料做衣服,需要拿材料到工厂里面加工,工厂就生产出衣服给你,衣服就是返回值。这其实跟数学中函数的概念是一样的。当然不是所有函数都有返回值,如一些函数只要完成某些特定的功能即可,不需要返回特定的值给主函数。如C语言中的printf这个方法,有些时候只希望屏幕输出一段加工过的文字,这就可以放在子函数中处理,而不需要将处理结果返回给主函数再处理,能够节省代码量。又如一些多线程的函数,很多都是没有返回值。具体情况具体分析www.zgxue.com防采集请勿采集本网。

一、前言

实例2:编写函数求一维整形数组的最大值与最小值,并把最大值与最小值返回给主调函数。方法:以指针方式传递该一维数组的地址,然后把数组的最大值与数组的第一个元素交换,把数组的最小值与最后一个元素

golang有很多新颖的特性,不知道大家的使用的时候,有没想过,这些特性是如何实现的?当然你可能会说,不了解这些特性好像也不影响自己使用golang,你说的也有道理,但是,多了解底层的实现原理,对于在使用golang时的眼界是完全不一样的,就类似于看过http的实现之后,再来使用http框架,和未看过http框架时的眼界是不一样的,当然,你如果是一名it爱好者,求知欲自然会引导你去学习。

执行真假值判断,根据逻辑计算的真假值,返回不同结果。可以使用函数 IF 对数值和公式进行条件检测。语法 IF(logical_test,value_if_true,value_if_false) Logical_test 表示计算结果为 TRUE 或 FALSE 的

二、这篇文章主要就分析两点:

偿债能力分析包括短期偿债能力分析和长期偿债能力分析两个方面。短期偿债能力主要表现在公司到期债务与可支配流动资产之间的关系,主要的衡量指标有流动比率和速动比率。1、流动比率是衡量企业

     1、golang多值返回的实现;

要求计算相关系数矩阵,单击Continue按钮e799bee5baa6e59b9ee7ad9431333363363534返回Factor Analysis主对话框。5单击主对话框中的Extraction 按钮,打开如下图所示的Factor Analysis:Extraction 子对话框

     2、golang闭包的实现;

我从别的地方看到的,用他这个方法可以很好解决 这是我们可以进入c:\\windows\\microsoft.net\\framework\\v2.0.50727\\Config文件夹,先备份machine.config,将其复制到其它地方,然后删除machine.

三、golang多值返回的实现

我们在学C/C++时,很多人应该有了解过C/C++函数调用过程,参数是通过寄存器di和si(假设就两个参数)传递给被调用的函数,被调用函数的返回结果只能是通过eax寄存器返回给调用函数,因此C/C++函数只能返回一个值,那么我们是不是可以想象,golang的多值返回是否可以通过多个寄存器来实现的,正如用多个寄存器来传参一样?

这也是一种办法,但是golang并没有采用;我的理解是引入多个寄存器来存储返回值,会引起多个寄存器用途的重新约定,这无疑增加了复杂度;可以这么说,golang的ABI与C/C++非常不一样;

在从汇编角度分析golang多值返回之前,需要先熟悉golang汇编代码的一些约定, golang官网 有说明,这里重点说明四个symbols,需要注意的是这里的寄存器是伪寄存器:

       1.FP 栈底寄存器,指向一个函数栈的顶部;

       2.PC 程序计数器,指向下一条执行指令;

       3.SB 指向静态数据的基指针,全局符号;

       4.SP 栈顶寄存器;

这里面最重要的就是FP和SP,FP寄存器主要用于取参数以及存返回值,golang函数调用的实现很大程度上都是依赖这两个寄存器,这里先给出结果,

+-----------+---\| 返回值2 | \+-----------+ \| 返回值1 | \+---------+-+ | 参数2 | 这些在调用函数中+-----------+ | 参数1 |   /+-----------+ /| 返回地址 | /+-----------+--\/-----fp值| 局部变量 | \| ... | 被调用数栈祯| | /+-----------+--/+---sp值

这个就是golang的一个函数栈,也是说函数传参是通过fp+offset来实现的,而多个返回值也是通过fp+offset存储在调用函数的栈帧中。

下面通过一个例子来分析

package mainimport "fmt"func test(i, j int) (int, int) {a:=i+ jb:=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函数的汇编代码

"".test t=1size=32value=0args=0x20locals=0x00x000000000(test.go:5) TEXT"".test(SB),$0-32//栈大小为32字节0x000000000(test.go:5)NOP0x000000000(test.go:5)NOP0x000000000(test.go:5)MOVQ"".i+8(FP),CX//取第一个参数i0x000500005(test.go:5)MOVQ"".j+16(FP),AX//取第二个参数j0x000a00010(test.go:5) FUNCDATA$0, gclocals·a8eabfc4a4514ed6b3b0c61e9680e440(SB)0x000a00010(test.go:5) FUNCDATA$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)0x000a00010(test.go:6)MOVQCX,BX//将i放入bx0x000d00013(test.go:6) ADDQAX,CX//i+j放入cx0x001000016(test.go:7) SUBQAX,BX//i-j放入bx //将返回结果存入调用函数栈帧0x001300019(test.go:8)MOVQCX,"".~r2+24(FP) //将返回结果存入调用函数栈帧0x001800024(test.go:8)MOVQBX,"".~r3+32(FP)0x001d00029(test.go:8)RET

由这个汇编代码可以看出来,在test函数内部,是通过fp+8取第一个参数,fp+16取第二个参数;然后将返回的第一个值存入fp+24,返回的第二个值存入fp+32,和我上述所说完全一致;golang函数调用过程,是通过fp+offset来实现传参和返回值,而不像C/C++都是通过寄存器实现传参和返回值;

但是,这里有个问题,我的变量都是int类型,为啥分配的都是8字节,这有待考证。

本来想通过查看main函数的栈帧来验证之前的结论,但是golang对小函数自动转为内联函数,因此你们可以自己编译出来看看,main函数内部是没有调用test函数的,而是将test函数的汇编代码直接拷贝进main函数执行了。

四、golang闭包的实现

之前有去看了下C++11的lambda函数的实现,其实实现原理就是仿函数;编译器在编译lambda函数时,会生成一个匿名的仿函数类,然后执行这个lambda函数时,会调用编译生成的匿名仿函数类重载函数调用方法,这个方法也就是lambda函数中定义的方法;其实golang闭包的实现和这个类似,我们通过例子来说明

packagemainimport"fmt"functest(aint)func(iint)int{returnfunc(iint)int{ a = a + ireturna }}funcmain(){ f := test(1) a := f(2) fmt.Println(a) b := f(3) fmt.Println(b)}

这个例子程序很简单,test函数传入一个整型参数a,返回一个函数类型;这个函数类型传入一个整型参数以及返回一个整型值;main函数调用test函数,返回一个闭包函数。

来看下test函数的汇编代码:

"".test t=1size=160value=0args=0x10locals=0x200x000000000(test.go:5) TEXT"".test(SB),$32-160x000000000(test.go:5)MOVQ(TLS),CX0x000900009(test.go:5) CMPQSP,16(CX)0x000d00013(test.go:5) JLS1420x000f00015(test.go:5) SUBQ$32,SP0x001300019(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),BX0x001a00026(test.go:5)MOVQBX, (SP)0x001e00030(test.go:5) PCDATA$0,$0 //生成一个int型对象,即a0x001e00030(test.go:5)CALLruntime.newobject(SB) //8(sp)即生成的a的地址,放入AX0x002300035(test.go:5)MOVQ8(SP),AX //将a的地址存入sp+24的位置0x002800040(test.go:5)MOVQAX,"".&a+24(SP) //取出main函数传入的第一个参数,即a0x002d00045(test.go:5)MOVQ"".a+40(FP),BP //将a放入(AX)指向的内存,即上述新生成的int型对象0x003200050(test.go:5)MOVQBP, (AX)0x003500053(test.go:6) LEAQ type.struct { F uintptr; a *int }(SB), BX0x003c00060(test.go:6)MOVQBX, (SP)0x004000064(test.go:6) PCDATA$0,$10x004000064(test.go:6)CALLruntime.newobject(SB) //8(sp)这就是上述生成的struct对象地址0x004500069(test.go:6)MOVQ8(SP),AX0x004a00074(test.go:6)NOP //test内部匿名函数地址存入BP0x004a00074(test.go:6) LEAQ"".test.func1(SB),BP //将匿名函数地址放入(AX)指向的地址,即给上述 //F uintptr赋值0x005100081(test.go:6)MOVQBP, (AX)0x005400084(test.go:6)MOVQAX,"".autotmp_0001+16(SP)0x005900089(test.go:6)NOP //将上述生成的整型对象a的地址存入BP0x005900089(test.go:6)MOVQ"".&a+24(SP),BP0x005e00094(test.go:6) CMPB runtime.writeBarrier(SB),$00x006500101(test.go:6)JNE$0,117 //将a地址存入AX指向内存+8, //即为上述结构体a *int赋值0x006700103(test.go:6)MOVQBP,8(AX) //将上述结构体的地址存入main函数栈帧中;0x006b00107(test.go:9)MOVQAX,"".~r1+48(FP)0x007000112(test.go:9) ADDQ$32,SP0x007400116(test.go:9)RET

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

类是有行为的数据,为闭包是有数据的行为;

也就是说闭包是有上下文的,我们以测试例子为例,通过test函数生成的闭包函数,都有各自的a,这个a就是闭包的上下文数据,而且这个a一直伴随着他的闭包函数,每调用一次,a都会发生变化;

我们分析了上述汇编代码,来看下闭包实现原理;在这个测试例子中,由于a是闭包的上下文数据,因此a必须在堆上分配,如果在栈上分配,函数结束,a也被回收了;然后会定义出一个匿名结构体:

type.struct{ F uintptr//这个就是闭包调用的函数指针 a *int//这就是闭包的上下文数据}

接着生成一个该对象,并将之前在堆上分配的整型对象a的地址赋值给结构体中的a指针,接下来将闭包调用的func函数地址赋值给结构体中F指针;这样,每生成一个闭包函数,其实就是生成一个上述结构体对象,每个闭包对象也就有自己的数据a和调用函数F;最后将这个结构体的地址返回给main函数;

来看下main函数获取闭包的过程;

"".main t=1size=528value=0args=0x0locals=0x880x000000000(test.go:12) TEXT"".main(SB),$136-00x000000000(test.go:12)MOVQ(TLS),CX0x000900009(test.go:12) LEAQ -8(SP),AX0x000e00014(test.go:12) CMPQAX,16(CX)0x001200018(test.go:12) JLS5060x001800024(test.go:12) SUBQ$136,SP0x001f00031(test.go:12) FUNCDATA$0, gclocals·f5be5308b59e045b7c5b33ee8908cfb7(SB)0x001f00031(test.go:12) FUNCDATA$1, gclocals·9d868b227cedd8dd4b1bec8682560fff(SB) //将参数1(f:=test(1))放入main函数栈顶0x001f00031(test.go:13)MOVQ$1, (SP)0x002700039(test.go:13) PCDATA$0,$0 //调用main函数生成闭包对象0x002700039(test.go:13)CALL"".test(SB) //将闭包对象的地址放入DX0x002c00044(test.go:13)MOVQ8(SP),DX //将参数2(a:=f(2))放入栈顶0x003100049(test.go:14)MOVQ$2, (SP)0x003900057(test.go:14)MOVQDX,"".f+56(SP) //将闭包对象的函数指针赋值给BX0x003e00062(test.go:14)MOVQ(DX),BX0x004100065(test.go:14) PCDATA$0,$1 //这里调用闭包函数,并且将闭包对象的地址也传进 //闭包函数,为了修改a嘛0x004100065(test.go:14)CALLDX,BX0x004300067(test.go:14)MOVQ8(SP),BX

很明显,main函数调用test函数获取的是闭包对象的地址,通过这个闭包对象地址找到闭包函数,然后执行这个闭包函数,并且把闭包对象的地址传进函数,这点和C++传this指针原理一样,为了修改成员变量a

最后看下test内部的匿名函数(闭包函数实现):

"".test.func1t=1size=32value=0args=0x10 locals=0x00x000000000(test.go:6) TEXT"".test.func1(SB), $0-160x000000000(test.go:6) NOP0x000000000(test.go:6) NOP0x000000000(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//将参数i存入R80x000700007(test.go:7) MOVQ"".i+8(FP), R8//a+i的值存入BP0x000c00012(test.go:7) ADDQ R8, BP//将a+i存入a的地址0x000f00015(test.go:7) MOVQ BP, (AX)//将a地址最新数据存入BP0x001200018(test.go:8) MOVQ (AX), BP//将a最新值作为返回值放入main函数栈中0x001500021(test.go:8) MOVQ BP,"".~r1+16(FP)0x001a00026(test.go:8) RET

闭包函数的调用过程:

      1、通过闭包对象地址获取闭包上下文数据a的地址;

      2、接着通过a的地址获取到a的值,并与参数i相加;

      3、将a+i作为最新值存入a的地址;

      4、将a最新值返回给main函数;

五、总结

这篇文章简单地从汇编角度分析了golang多值返回和闭包的实现;

      多值返回主要是通过fp寄存器+offset获取参数以及存入返回值实现;

      闭包主要是通过在编译时生成包含闭包函数和闭包上下文数据的结构体实现;

以上就是这篇文章的全部内容,希望对大家学习或只用golang能有一定的帮助,如果有疑问大家可以留言交流。

A.偿债能力分析流动比率=流动资产/流动负债 流动比率可以反映短期偿债能力。一般认为生产企业合理的最低流动比率是2。影响流动比率的主要因素一般认为是营业周期、流动资产中的应收帐款数额和存货周转速度。速动比率=(流动资产-存货)/流动负债 由于种种原因存货的变现能力较差,因此把存货从流动资产种减去后得到的速动比率反映的短期偿债能力更令人信服。一般认为企业合理的最低速动比率是1。但是,行业对速动比率的影响较大。比如,商店几乎没有应收帐款,比率会大大低于1。影响速动比率的可信度的重要因素是应收帐款的变现能力。保守速动比率(超速动比率)=(货币资金+短期投资+应收票据+应收帐款)/流动负债 进一步去掉通常与当期现金流量无关的项目如待摊费用等。现金比率=(货币资金/流动负债)现金比率反应了企业偿还短期债务的能力。应收帐款周转率=销售收入/平均应收帐款 表达年度内应收帐款转为现金的平均次数。如果周转率太低则影响企业的短期偿债能力。应收帐款周转天数=360天/应收帐款周转率 表达年度内应收帐款转为现金的平均天数。影响企业的短期偿债能力。B.资本结构分析股东权益比率=股东权益总额/资产总额×100%反映所有者提供的资本在总资产中的比重,反映企业的基本财务结构是否稳定。一般来说比率高是低风险、低报酬的财务结构,比率低是高风险、高报酬的财务结构。资产负债比率=负债总额/资产总额×100%反映总资产中有多大比例是通过借债得来的。资本负债比率=负债合计/股东权益期末数×100%它比资产负债率这一指标更能准确地揭示企业的偿债能力状况,因为公司只能通过增加资本的途径来降低负债率。资本负债率为200%为一般的警戒线,若超过则应该格外关注。长期负债比率=长期负债/资产总额×100%判断企业债务状况的一个指标。它不会增加到企业的短期偿债压力,但是它属于资本结构性问题,在经济衰退时会给企业带来额外风险。有息负债比率=(短期借款+一年内到期的长期负债+长期借款+应付债券+长期应付款)/股东权益期末数×100%无息负债与有息负债对利润的影响是完全不同的,前者不直接减少利润,后者可以通过财务费用减少利润;因此,公司在降低负债率方面,应当重点减少有息负债,而不是无息负债,这对于利润增长或扭亏为盈具有重大意义。在揭示公司偿债能力方面,100%是国际公认的有息负债对资本的比率的资本安全警戒线。C.经营效率分析净资产调整系数=(调整后每股净资产-每股净资产)/每股净资产 调整后每股净资产=(股东权益-3年以上的应收帐款-待摊费用-待处理财产净损失-递延资产)/普通股股数 减掉的是四类不能产生效益的资产。净资产调整系数越大说明该公司的资产质量越低。特别是如果该公司在系数很大的条件下,其净资产收益率仍然很高,则要深入分析。营业费用率=营业费用/主营业务收入×100%财务费用率=财务费用/主营业务收入×100%反映企业财务状况的指标。三项费用增长率=(上期三项费用合计-本期三项费用合计)/本期三项费用合计 三项费用合计=营业费用+管理费用+财务费用 三项费用之和反应了企业的经营成本如果三项费用合计相对于主营业务收入大幅增加(或减少)则说明企业产生了一定的变化,要提起注意。存货周转率=销货成本×2/(期初存货+期末存货)存货周转天数=360天/存货周转率 存货周转率(天数)表达了公司产品的产销率,如果和同行业其它公司相比周转率太小(或天数太长),就要注意公司产品是否能顺利销售。固定资产周转率=销售收入/平均固定资产 该比率是衡量企业运用固定资产效率的指标,指标越高表示固定资产运用效果越好。总资产周转率=销售收入/平均资产总额 该指标越大说明销售能力越强。主营业务收入增长率=(本期主营业务收入-上期主营业务收入)/上期主营业务收入×100%一般当产品处于成长期,增长率应大于10%。其他应收帐款与流动资产比率=其他应收帐款/流动资产 其他应收帐款主要核算与生产经营销售活动无关的款项来往,一般应该较小。如果该指标较高则说明流动资金运用在非正常经营活动的比例高,就应该注意是否与关联交易有关。D.盈利能力分析营业成本比率=营业成本/主营业务收入×100%在同行之间,营业成本比率最具有可比性,原因是原材料消耗大体一致,生产设备及工资支出也较为一致,发生在这一指标上的差异可以说明各公司之间在资源优势、区位优势、技术优势及劳动生产率等方面的状况。那些营业成本比率较低的同行,往往就存在某种优势,而且这些优势也造成了盈利能力上的差异。相反,那些营业成本比率较高的同行,在盈利能力不免处于劣势地位。营业利润率=营业利润/主营业务收入×100%销售毛利率=(主营业务收入-主营业务成本)/主营业务收入×100%税前利润率=利润总额/主营业务收入×100%税后利润率=净利润/主营业务收入×100%这几个指标都是从某一方面反应企业的获利能力。资产收益率=净利润×2/(期初资产总额+期末资产总额)×100%资产收益率反应了企业的总资产利用效率,或者说是企业所有资产的获利能力。净资产收益率=净利润/净资产×100%又称股东权益收益率,这个指标反应股东投入的资金能产生多少利润。经常性净资产收益率=剔除非经常性损益后的净利润/股东权益期末数×100%一般来说资产只能产生“剔除非经常性损益后的净利润”所以用这个指标来衡量资产状况更加准确。主营业务利润率=主营业务利润/主营业务收入×100%一个企业如果要实现可持续性发展,主营业务利润率处于同行业前列并保持稳定十分重要。但是如果该指标异忽寻常地高于同业平均水平也应该谨慎了。固定资产回报率=营业利润/固定资产净值×100%总资产回报率=净利润/总资产期末数×100%经常性总资产回报率=剔除非经常性损益后的净利润/总资产期末数×100%这几项都是从某一方面衡量资产收益状况。E.投资收益分析市盈率=每股市场价/每股净利润 净资产倍率=每股市场价/每股净资产值 资产倍率=每股市场价/每股资产值F.现金保障能力分析销售商品收到现金与主营业务收入比率=销售商品、提供劳务收到的现金/主营业务收入×100%正常周转企业该指标应大于1。如果指标较低,可能是关联交易较大、虚构销售收入或透支将来的销售,都可能会使来年的业绩大幅下降。经营活动产生的现金流量净额与净利润比率=经营活动产生的现金流量净额/净利润×100%净利润直接现金保障倍数=(营业现金流量净额-其他与经营活动有关的现金流入+其他与经营活动有关的现金流出)/主营业务收入×100%营业现金流量净额对短期有息负债比率=营业现金流量净额/(短期借款+1年内到期的长期负债)×100%每股现金及现金等价物净增加额=现金及现金等价物净增加额/股数 每股自由现金流量=自由现金流量/股数(自由现金流量=净利润+折旧及摊销-资本支出-流动资金需求-偿还负债本金+新借入资金)内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • 简单了解go语言中函数作为值以及函数闭包的使用
  • 举例讲解go语言中函数的闭包使用
  • javascript.the.good.parts阅读笔记(二)作用域&闭包&减缓全局空间污染
  • 深入理解go语言中的闭包
  • 在mac os上安装go语言编译器的方法
  • go语言中利用http发起get和post请求的方法示例
  • 使用go添加https的实现代码示例
  • golang 使用http client下载文件的实现方法
  • go语言中struct的匿名属性特征实例分析
  • go语言实现markdown解析库的方法示例
  • go语言操作redis连接池的方法
  • golang编程入门之http请求天气实例
  • 总结go语言中defer的使用和注意要点
  • go语言在linux环境下输出彩色字符的方法
  • 利用财务比率进行分析,可以分析的方面有哪些?
  • 请问C语言中的返回值是什么意思,为什么要有返回值,解释一下,越通俗易懂越好!
  • 在Java中,return返回值的意义,为什么要用返回值?
  • r prcomp返回值怎么分析
  • c语言中函数能返回一个数组吗
  • excel中档一个数字等于一列数字中的一个值时,返回为真怎么弄
  • 如何分析公司的偿债能力
  • spss在主成分分析中,如何得出特征值,贡献率和累计贡献率,补充图中的最后三行
  • machine.config 分析器返回错误 0xC00CE556
  • 什么是数据分析 有什么作用?
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全vbsdos/bathtahtcpythonperl游戏相关vba远程脚本coldfusionruby专题autoitseraphzonepowershelllinux shellluagolangerlang其它首页golang简单了解go语言中函数作为值以及函数闭包的使用举例讲解go语言中函数的闭包使用javascript.the.good.parts阅读笔记(二)作用域&闭包&减缓全局空间污染深入理解go语言中的闭包在mac os上安装go语言编译器的方法go语言中利用http发起get和post请求的方法示例使用go添加https的实现代码示例golang 使用http client下载文件的实现方法go语言中struct的匿名属性特征实例分析go语言实现markdown解析库的方法示例go语言操作redis连接池的方法golang编程入门之http请求天气实例总结go语言中defer的使用和注意要点go语言在linux环境下输出彩色字符的方法go语言中的array、slice、map和sgo语言的gopath与工作目录详解go语言string,int,int64 ,floago语言interface详解五步让你成为go 语言高手go语言命令行操作命令详细介绍go语言编程中字符串切割方法小结我放弃python转go语言的9大理由(go语言创建、初始化数组的常见方go语言数组和切片实例详解golang如何调用python代码详解go语言对字符串进行sha1哈希运算的方法go语言的os包中常用函数初步归纳golang 函数以及函数和方法的详解及区别浅谈go语言renderer包代码分析使用docker构建golang线上部署环境的步骤用golang实现一个定时器任务队列实例利用golang进行opencv学习和开发的步骤golang极简入门教程(一):基本概念golang学习笔记(三):控制流
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved