Golang底层原理剖析之函数调用栈-传参和返回值

简介: Golang底层原理剖析之函数调用栈-传参和返回值

defer与return时机

return赋值和返回是两个步骤,不是原子操作,如果有defer会插在两个步骤中:

  1. 返回值赋值(return value)
  2. defer语句 //可有可无
  3. 返回值返回

传值的swap函数

我们通过函数调用栈看看问题到底出在哪

假设main函数栈帧在这里,先分配局部变量locals,这里函数调用没有返回值,所以局部后面就是给被调用函数传入的参数args,注意参数入栈顺序由右到左,返回值也是一样,这样被调用函数通过sp+偏移寻址就比较方便了,调用者栈帧后面存的是下一条指令的地址,在下面分配的就是swap函数栈帧了,当swap函数执行到a,b=b,a时,要交换两个参数的值

把值交换一下

现在,交换失败的原因找到了,调用者的局部变量a和b在这里,交换的并不是它们

传指针的swap函数

我们通过函数调用栈看看和上一次有什么不同

main函数栈帧先分配局部变量,然后分配参数空间,参数是指针,传参都是值拷贝,这里拷贝的就是a和b的地址,在后面是返回地址以及swap函数栈帧

swap执行到*a,*b=*b,*a时,交换的是这两个指针指向的数据,也就是这两个地址的数据,所以这一次能交换成功

匿名返回值函数

通常我们认为返回值是通过寄存器传递的,但是go语言支持多返回值,所以在栈上分配返回值空间更合适

这里main函数调用incr函数,然后赋给局部变量b,来看看函数调用栈的情况。

main函数栈帧,先是局部变量a=0,b=0,然后是incr的返回值,初始化为类型零值,然后是参数,传参值拷贝,最后是返回地址。到incr函数栈帧这里,保存调用者main的栈帧地址后,初始化局部变量b

执行到这里,要把参数a自增1,而参数a在这

下一步,把参数a赋给局部变量b。到return这里,必须要明确一个关键问题。我们说过函数最后有编译器插入的指令,负责释放函数栈帧,恢复到调用者栈,但在这之前要给返回值赋值并执行defer函数,那谁先?谁后?答案是先赋值

所以执行到return b这里,会先把局部变量b的值拷贝到返回值空间

然后再执行注册的defer函数,defer函数里,这一步a再次自增1,下一步局部变量b也自增1,然后incr结束。

返回值为1 ,赋给main函数的局部变量b,所以最后会输出0和1

具名返回值函数

其他都不变,只把这里的局部变量b,改成命名返回值,看看有什么不同

main函数栈帧与上一例完全相同,到incr函数栈帧这里,没有局部变量,当执行到a++时,参数a自增1

return这里,先把参数a赋给返回值b

然后执行defer函数,参数a再次自增1,下一步,返回值b也自增1,然后incr结束,返回值最终为2,所以main的局部变量b赋值为2,最终输出0和2

调用多个函数的小问题

如果一个函数A调用了两个函数B和C。但是这两个函数的参数和返回值,占用的空间并不相同,我们知道Go语言的函数栈帧是一次性分配的,如果局部变量占这么大,这后面还要以最大的参数加返回值空间为标准来分配,才能满足所有被调函数的需求

B的参数和返回值可以把这里占满没有问题


目录
相关文章
|
4月前
|
存储 安全 测试技术
GoLang协程Goroutiney原理与GMP模型详解
本文详细介绍了Go语言中的Goroutine及其背后的GMP模型。Goroutine是Go语言中的一种轻量级线程,由Go运行时管理,支持高效的并发编程。文章讲解了Goroutine的创建、调度、上下文切换和栈管理等核心机制,并通过示例代码展示了如何使用Goroutine。GMP模型(Goroutine、Processor、Machine)是Go运行时调度Goroutine的基础,通过合理的调度策略,实现了高并发和高性能的程序执行。
324 29
|
4月前
|
负载均衡 算法 Go
GoLang协程Goroutiney原理与GMP模型详解
【11月更文挑战第4天】Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理,创建和销毁开销小,适合高并发场景。其调度采用非抢占式和协作式多任务处理结合的方式。GMP 模型包括 G(Goroutine)、M(系统线程)和 P(逻辑处理器),通过工作窃取算法实现负载均衡,确保高效利用系统资源。
|
6月前
|
算法 安全 测试技术
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
100 1
golang 栈数据结构的实现和应用
|
6月前
|
Go
Golang语言之函数(func)进阶篇
这篇文章是关于Golang语言中函数高级用法的教程,涵盖了初始化函数、匿名函数、闭包函数、高阶函数、defer关键字以及系统函数的使用和案例。
135 3
Golang语言之函数(func)进阶篇
|
6月前
|
Go
Golang语言之函数(func)基础篇
这篇文章深入讲解了Golang语言中函数的定义和使用,包括函数的引入原因、使用细节、定义语法,并通过多个案例展示了如何定义不返回任何参数、返回一个或多个参数、返回值命名、可变参数的函数,同时探讨了函数默认值传递、指针传递、函数作为变量和参数、自定义数据类型以及返回值为切片类型的函数。
148 2
Golang语言之函数(func)基础篇
|
10月前
|
Go
golang中置new()函数和make()函数的区别
golang中置new()函数和make()函数的区别
|
6月前
|
Unix Go
Golang语言标准库time之日期和时间相关函数
这篇文章是关于Go语言日期和时间处理的文章,介绍了如何使用Go标准库中的time包来处理日期和时间。
133 3
|
7月前
|
存储 关系型数据库 Go
SOLID原理:用Golang的例子来解释
SOLID原理:用Golang的例子来解释
|
7月前
|
算法 NoSQL 关系型数据库
熔断原理与实现Golang版
熔断原理与实现Golang版
|
7月前
|
存储 人工智能 Go
golang 反射基本原理及用法
golang 反射基本原理及用法
54 0