0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

详细介绍go语言中的闭包的实现

马哥Linux运维 来源:tiancaiamao 作者:tiancaiamao 2021-10-20 16:18 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

什么是闭包?什么场景下会用闭包
本文对go 语言中的闭包做了详细介绍。

闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

Go中的闭包

闭包是函数式语言中的概念,没有研究过函数式语言的用户可能很难理解闭包的强大,相关的概念超出了本书的范围。Go语言是支持闭包的,这里只是简单地讲一下在Go语言中闭包是如何实现的。

funcf(iint)func()int{
returnfunc()int{
i++
returni
}
}

函数f返回了一个函数,返回的这个函数,返回的这个函数就是一个闭包。这个函数中本身是没有定义变量i的,而是引用了它所在的环境(函数f)中的变量i。

c1:=f(0)
c2:=f(0)
c1()//referencetoi,i=0,return1
c2()//referencetoanotheri,i=0,return1

c1跟c2引用的是不同的环境,在调用i++时修改的不是同一个i,因此两次的输出都是1。函数f每进入一次,就形成了一个新的环境,对应的闭包中,函数都是同一个函数,环境却是引用不同的环境。

变量i是函数f中的局部变量,假设这个变量是在函数f的栈中分配的,是不可以的。因为函数f返回以后,对应的栈就失效了,f返回的那个函数中变量i就引用一个失效的位置了。所以闭包的环境中引用的变量不能够在栈上分配。

escape analyze

在继续研究闭包的实现之前,先看一看Go的一个语言特性:

funcf()*Cursor{
varcCursor
c.X=500
noinline()
return&c
}

Cursor是一个结构体,这种写法在C语言中是不允许的,因为变量c是在栈上分配的,当函数f返回后c的空间就失效了。但是,在Go语言规范中有说明,这种写法在Go语言中合法的。语言会自动地识别出这种情况并在堆上分配c的内存,而不是函数f的栈上。

为了验证这一点,可以观察函数f生成的汇编代码:

MOVQ$type."".Cursor+0(SB),(SP)//取变量c的类型,也就是Cursor
PCDATA$0,$16
PCDATA$1,$0
CALL,runtime.new(SB)//调用new函数,相当于new(Cursor)
PCDATA$0,$-1
MOVQ8(SP),AX//取c.X的地址放到AX寄存器
MOVQ$500,(AX)//将AX存放的内存地址的值赋为500
MOVQAX,"".~r0+24(FP)
ADDQ$16,SP

识别出变量需要在堆上分配,是由编译器的一种叫escape analyze的技术实现的。如果输入命令:

gobuild--gcflags=-mmain.go

可以看到输出:

./main.gomovedtoheap:c
./main.go&cescapestoheap

表示c逃逸了,被移到堆中。escape analyze可以分析出变量的作用范围,这是对垃圾回收很重要的一项技术。

闭包结构体

回到闭包的实现来,前面说过,闭包是函数和它所引用的环境。那么是不是可以表示为一个结构体呢:

typeClosurestruct{
Ffunc()()
i*int
}

事实上,Go在底层确实就是这样表示一个闭包的。让我们看一下汇编代码:

funcf(iint)func()int{
returnfunc()int{
i++
returni
}
}


MOVQ$type.int+0(SB),(SP)
PCDATA$0,$16
PCDATA$1,$0
CALL,runtime.new(SB)//是不是很熟悉,这一段就是i=new(int)
...
MOVQ$type.struct{Fuintptr;A0*int}+0(SB),(SP)//这个结构体就是闭包的类型
...
CALL,runtime.new(SB)//接下来相当于new(Closure)
PCDATA$0,$-1
MOVQ8(SP),AX
NOP,
MOVQ$"".func·001+0(SB),BP
MOVQBP,(AX)//函数地址赋值给Closure的F部分
NOP,
MOVQ"".&i+16(SP),BP//将堆中new的变量i的地址赋值给Closure的值部分
MOVQBP,8(AX)
MOVQAX,"".~r1+40(FP)
ADDQ$24,SP
RET,

其中func·001是另一个函数的函数地址,也就是f返回的那个函数。

小结

  1. Go语言支持闭包
  2. Go语言能通过escape analyze识别出变量的作用域,自动将变量在堆上分配。将闭包环境变量在堆上分配是Go实现闭包的基础。
  3. 返回闭包时并不是单纯返回一个函数,而是返回了一个结构体,记录下函数返回地址和引用的环境中的变量地址。

转自:

tiancaiamao.gitbooks.io/go-internals/content/zh/03.6.html

编辑:jq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    183

    文章

    7642

    浏览量

    144649
  • 函数
    +关注

    关注

    3

    文章

    4406

    浏览量

    66858
  • 代码
    +关注

    关注

    30

    文章

    4942

    浏览量

    73160
  • go语言
    +关注

    关注

    1

    文章

    159

    浏览量

    9630

原文标题:Golang 闭包的实现

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    C语言的分支结构介绍

    1.简单if语句 C语言中的分支结构语句中的if条件语句。 简单if语句的基本结构如下: 代码语言:javascript if(表达式) { 执行代码块; } 其语义是:如果表达式的值为真,则执行其后的语句,否则不执行该语句。 注意:if()后面没有分号,直
    发表于 11-25 07:48

    C语言的常量介绍

    、-13; 实型常量:13.33、-24.4; 字符常量:‘a’、‘M’ 字符串常量:”I love china!” 在C语言中,可以用一个标识符来表示一个常量,称之为符号常量。符号常量在使用之前必须先
    发表于 11-24 07:12

    房产数据平台安家go获取地区列表数据的API接口

    ​ 在房产数据平台“安家go”中,获取地区列表数据是一个常见需求,它允许开发者访问全国或特定区域的行政区划信息,如省、市、区县等。这对于构建房产搜索、数据分析或地图应用至关重要。本文将详细介绍
    的头像 发表于 11-21 14:38 177次阅读
    房产数据平台安家<b class='flag-5'>go</b>获取地区列表数据的API接口

    C语言的printf基本用法介绍

    也是代码的一部分,但是它并不会给程序带来任何影响,编译器在编译阶段会忽略注释的内容,或者说删除注释的内容。我在《C语言标识符、关键字和注释》一节中做了详细讲解。 3) money 的输出值并不是
    发表于 11-12 07:04

    在Linux系统下编译C语言仿真蜂鸟E203(二)

    运行操作在Makefile中有详细介绍 make run_test之后在终端中打印跑分信息如下所示,其跑分为3.05 其打印原理时利用了verilog语言中的打印函数实现的: 在u
    发表于 10-24 13:38

    Windows环境下32位汇编语言中文资料

    电子发烧友网站提供《Windows环境下32位汇编语言中文资料.rar》资料免费下载
    发表于 06-30 15:14 0次下载

    提高篇——C语言核心技术(中文版)

    严谨的术语表介绍C语言;第二部分描述标准链接库;第三部分介绍GNU软件中常用的编译和测试工具。 获取完整文档资料可下载附件哦!!!!如果内容有帮助可以关注、点赞、评论支持一下哦~
    发表于 06-13 16:39

    深入理解C语言:C语言循环控制

    改变程序的执行流程,使代码更加灵活和可控。本文将详细介绍这些语句的作用及其应用场景,并通过示例代码进行说明。Part.1break语句C语言中break语句有两种
    的头像 发表于 04-29 18:49 1749次阅读
    深入理解C<b class='flag-5'>语言</b>:C<b class='flag-5'>语言</b>循环控制

    从 Java 到 Go:面向对象的巨人与云原生的轻骑兵

    Go 语言在 2009 年被 Google 推出,在创建之初便明确提出了“少即是多(Less is more)”的设计原则,强调“以工程效率为核心,用极简规则解决复杂问题”。它与 Java 语言生态
    的头像 发表于 04-25 11:13 515次阅读

    C语言的历史及程序介绍

    电子发烧友网站提供《C语言的历史及程序介绍.pdf》资料免费下载
    发表于 04-09 16:10 0次下载

    接触器常开常是什么意思

    在电气自动化控制系统中,接触器作为一种重要的控制元件,广泛应用于电动机启动、停止控制,以及电力线路的切换和保护。接触器的常开和常触点是其核心组成部分,对于实现电路的逻辑控制和保护起着至关重要的作用
    的头像 发表于 02-06 16:21 7565次阅读

    EE-62:在C语言中访问短字内存

    电子发烧友网站提供《EE-62:在C语言中访问短字内存.pdf》资料免费下载
    发表于 01-07 14:02 0次下载
    EE-62:在C<b class='flag-5'>语言中</b>访问短字内存

    EE-128:C语言中的DSP:从C调用汇编类成员函数

    电子发烧友网站提供《EE-128:C语言中的DSP:从C调用汇编类成员函数.pdf》资料免费下载
    发表于 01-07 13:48 0次下载
    EE-128:C<b class='flag-5'>语言中</b>的DSP:从C调用汇编类成员函数

    霍尔元件常开和常怎么区分

    霍尔元件是一种基于霍尔效应的磁传感器,它通过感应磁场的变化来输出相应的电信号。在讨论如何区分霍尔元件的常开和常之前,我们需要了解一些基本概念。霍尔元件一般有NPN和PNP两种输出类型,而常开和常
    的头像 发表于 12-18 10:08 1919次阅读

    影目科技发布全球首款同传翻译眼镜INMO GO2

    近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言
    的头像 发表于 12-11 10:00 1966次阅读