关于函数调用和尾递归的一点认识

本文介绍了函数调用的原理,涉及控制栈、活动记录等概念,并通过计算阶乘的例子展示了活动记录的压栈和弹出过程。接着探讨了尾递归的概念,指出尾递归在内存占用和效率上的优势,以及Scheme语言对尾递归的支持。最后讨论了如何将非尾递归转化为尾递归,以及这种转化的优缺点。
摘要由CSDN通过智能技术生成

函数调用

在大多数支持块结构的程序设计语言都支持函数或者子程序(函数和子程序的区别在于函数有返回值而子程序没有,在这里我们不区分这两个概念)。在进行函数调用和从函数返回时通常由一个被称为控制栈的运行时刻栈进行管理。每一个活跃的函数在控制栈中都会有一个相对应的活动记录,有时也称为栈帧。活动记录存储着函数调用时传递的参数信息和从函数返回时返回值与控制跳转的信息。

 

函数的活动记录需要包括下面的信息

  • 控制链(control link):指向控制栈中前一个活动记录的指针;
  • 访问链(access link):指向源程序中最近的外层块对应的活动记录,用于维护静态作用域(本文中不讨论);
  • 返回地址:函数调用结束后被执行的第一条指令地址(本文讨论中将忽略);
  • 返回结果地址:存放函数返回值位置的地址;
  • 实际参数:函数调用时传递的参数值;
  • 局部变量:函数体中声明的局部变量;
  • 临时存储区:在函数执行过程中产生的临时结果。

不同语言的不同实现中对上述信息的存放顺序和存放方式可能不一样。在这里我们按照上面说明的顺序来进行讨论。

 

每当一个函数被调用时,就创建一个相对应的活动记录,并压入控制栈中;而当从一个函数调用结束时,在控制栈中与该函数对应的活动记录(即栈顶的活动记录)就会被弹出控制栈。每个函数所对应的活动记录的大小是不一致的,而堆栈寄存器每次记录的都只是栈顶的活动记录,所以在每个活动记录中需要一个控制链来记录前一个活动记录。

 <

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值