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

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

3天内不再提示

当函数执行完毕后,如何返回调用处?

AGk5_ZLG_zhiyua 来源:未知 作者:佚名 2017-09-14 14:27 次阅读

周立功教授数年之心血之作《程序设计与数据结构》以及《面向AMetal框架与接口编程(上)》,书本内容公开后,在电子行业掀起一片学习热潮。经周立功教授授权,本公众号特对《程序设计与数据结构》一书内容进行连载,愿共勉之。

第二章为程序设计技术,本文为2.3 栈与函数返回。

当函数执行完毕后,如何返回调用处呢?由于该函数可能会被多次调用,且每次调用的地方很可能不一样,这样被调用函数也就不可能知道自己该返回到哪里,因此在调用函数时必须告诉被调用函数应返回到哪里?

>>> 2.3.1 堆栈

为了保存变量(数据),通常计算机会提供非常多的内存。为了便于管理内存,将所有变量使用的内存称为栈,而将未分配的内存区域称为堆。这些未分配的内存区域,程序员可以块为单位请求它。这部分内存是由操作系统管理的,一旦一块内存被分配出去,它只能由分配了这块内存的原始代码使用,并使用指针访问这块内存。由于内存是稀缺资源,当程序不再需要该内存时,都应该释放回去。如果不这样做,程序将会耗光内存,导致运行速度下降甚至崩溃。这就是因为程序员没有释放本应释放的内存,造成了所谓的内存泄漏。

堆和栈是两种常用的数据结构,主要用于数据的动态存储。当程序执行时,栈中存储的是程序的执行过程,比如,main()函数的局部变量argc和argv都在栈中,而使用malloc()函数动态分配的内存是存储在堆中的,堆栈共享同一块内存区域。通常程序栈占据这块区域的下部,而堆用的是上部。当调用函数时,函数的栈帧被推到栈上,栈向上“长出”一个栈帧。当函数终止时,其栈帧从程序栈弹出。虽然栈所使用的内存不会被清理,但最终可能会被推到程序栈上的另一个栈帧覆盖。动态分配的内存来自堆,堆向下生长。随着内存的分配和释放,堆中会布满碎片。尽管堆是向下生长的,但这只是大体方向,实际上内存可能在堆上的任意位置分配。

平常大家所说的“堆栈”主要是指栈,计算机在硬件上直接支持栈。在计算机科学中,栈是一个抽象的概念。它的抽象行为特征是栈可以存储相同类型的数据,通常又将栈中的数据称为元素。只允许向栈中压入一个元素(即入栈push),或从栈中删除一个元素(即出栈pop),且元素按照“后进先出”原则处理(last in,first out,LIFO),禁止测试或修改不在栈顶的元素。

图2.7 四种栈示意图

如图2.7所示为通用计算机4种形式的栈,分别称之为满递减堆栈、空递减堆栈、满递增堆栈和空递增堆栈,这些都是栈的物理结构。其中的“递减”是指数据入栈时堆栈指针的值减少,即堆栈从高地址向下增长,就像钟乳石一样。“递增”是指数据入栈时堆栈指针的值增加,即堆栈从低地址向上增长,就像石笋一样。而“满”是指SP指向的存储单元保存最后入栈的数据;“空”是指SP指向的存储单元将保存下一个入栈的数据。4种形式的栈都对应相同的逻辑数据结构,本书后续章节除非特殊说明,否则均以“满递增堆栈”为例。

>>> 2.3.2 入栈与出栈

假设允许入栈和出栈数据为int,即sp为(int *)类型变量。如果入栈的数据小于sizeof(int)个字节,则需要将其转换成int类型数据才能入栈,且出栈后也要进行相应的类型转换。对于入栈的数据大于sizeof(int)个字节,则只能拆分数据,一次入栈数据的一部分,通过多次入栈完成整个数据的入栈;而出栈这个数据也要多次,全部出栈后再组合成原始数据。

1. 入栈(push)操作

如果将sp当作(int *)类型的变量,则对于满递增堆栈来说,将数据data入栈用C语言描述如下(详见图2.8):

图2.8 入栈操作示意图

如果data的长度大于sizeof(int),则需要将数据拆分后多次入栈,入栈的顺序可以先低位后高位,也可以反过来。如果入栈的顺序为先低位后高位,其示例详见程序清单 2.27。

程序清单 2.27 先低位后高位顺序入栈示例

这里假设data可以象整数一样移位,且sizeof(data)是sizeof(int)的4倍。

2. 出栈(pop)操作

如果将sp当作(int *)类型的变量,则对于满递增堆栈来说,将数据出栈用C语言描述如下(假设出栈的数据保存到变量data中,详见图2.9):

图2.9 出栈操作示意图

如果出栈数据data的长度大于sizeof(int),则需要多次出栈后拼接数据,其拼接的顺序为入栈的反序。如果入栈的顺序为先低位后高位,详见程序清单 2.28。

程序清单 2.28 先高位后低位顺序出栈示例

这里假设data可以象整数一样进行位操作,且sizeof(data)是sizeof(int)的4倍。

>>> 2.3.3 函数的调用与返回

在讨论ADT栈之前,首先看一种用于处理程序运行时的函数调用的系统栈。每当函数被调用时,系统首先创建一个称作活动记录或栈帧的结构,将其放在系统栈的栈顶。初始时,被调函数的活动记录只包含一个指向前一个活动记录的指针和一个返回地址。前一个活动记录的指针指向调用函数的活动记录,而返回地址包含的是函数调用结束后下一条执行语句的地址。因为在任何时刻只有一个函数被执行,所以被执行的函数就是活动记录位于系统栈栈顶的函数。

如果该函数又调用其它函数,那么函数中的局部变量(静态局部变量除外)及其参数也将加到其活动记录中,然后为被调函数创建一个新的活动记录并存放在系统栈栈顶的函数。当被调函数结束时,删除该活动记录。此时调用函数的活动记录又位于系统栈的栈顶,继续运行该函数。

C语言通过硬件栈保存函数的返回地址,被调用函数将返回地址出栈到程序计数器PC中,以返回到调用点,其示例代码详见程序清单2.29。

程序清单2.29 函数的调用与返回示例

对于程序清单2.29(10)来说,用C语言描述如下:

对于程序清单2.29(5)来说,用C语言描述如下:

由此可见,当调用函数时,将主程序代码行的下一条指令的地址保存到栈中;当函数返回时,程序就会从栈中获取该地址,并从那一点继续向下执行。在函数调用了其它函数的情况下,将每一个返回地址都放到栈中;当函数结束时,就可以找到它们在栈中的地址。

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

    关注

    0

    文章

    171

    浏览量

    19532

原文标题:周立功:栈与函数返回的应用

文章出处:【微信号:ZLG_zhiyuan,微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    一文详解python调用函数

    函数被定义后,本身是不会自动执行的,只有在被调用后,函数才会被执行,得到相应的结果。但是在 Python 中我们要注意一个关键点,就是Pyt
    发表于 10-01 10:45 238次阅读

    如何查看及更改函数/函数块的调用环境

    是循环执行,当一个功能块被多个外部函数/函数调用时,我们应如何查看某一次调用时的内部变量呢?这涉及到
    的头像 发表于 11-17 09:08 443次阅读
    如何查看及更改<b class='flag-5'>函数</b>/<b class='flag-5'>函数</b>块的<b class='flag-5'>调用</b>环境

    代码未从函数调用返回

    调用函数,但是调用我的配置时钟函数时,它执行函数
    发表于 09-16 07:11

    调用TCPIP_DHCP_IsServerDetected()函数时总是返回0

    Harmony v1.09在DHCP客户端模式下,在设备从DHCP服务器成功获取IP地址之后,调用TCPIP_DHCP_IsServerDetected()函数时,它总是返回0。(传
    发表于 04-06 14:37

    labview怎么调用exe并获取exe的返回

    求教各位大佬,我想用labview调用exe(任意语言开发的exe文件),exe文件执行完毕,怎么获取exe的返回值?
    发表于 04-07 17:02

    C++教程之函数的递归调用

    C++教程之函数的递归调用执行函数 f 的过程中,又要调用 f 函数本身,称为
    发表于 05-15 18:00 35次下载

    高效的C编程之函数调用

    指令BL或MOV pc,lr一般只需要6个指令周期(ARM7上)。 在函数的入口和出口使用多寄存器加载/存储指令LDM和STM(Thumb指令使用PUSH和POP)提高函数体的执行效率。 ARM体系结构过程
    发表于 10-17 16:49 6次下载
    高效的C编程之<b class='flag-5'>函数</b><b class='flag-5'>调用</b>

    浅谈C语言return语句和main 函数返回

    函数中,如果碰到return 语句,那么程序就会返回调用函数的下一条语句执行,也就是说跳出函数
    发表于 05-10 10:53 4990次阅读

    return-函数返回值是什么

    return关键字后接变量名或表达式可以将函数的计算结果返回调用处。变量或表达式等同于接收果汁、豆浆的杯子。如果函数没有返回值,retur
    的头像 发表于 02-23 10:52 800次阅读
    return-<b class='flag-5'>函数</b>的<b class='flag-5'>返回</b>值是什么

    什么是函数返回值?

    函数返回值是函数调用后,执行调用函数内代码后所
    的头像 发表于 04-04 17:21 3570次阅读

    python函数函数之间的调用

    中没有调用执行y()函数,只是执行了return y。而y变量也没有值,所以整个程序的返回值就为无结果。 3.2 第二种情况 程序代码如下:
    的头像 发表于 10-04 17:17 355次阅读

    C语言函数返回1和返回0究竟哪个好?

    C语言函数返回1和返回0究竟哪个好? 在C语言中,很多函数需要返回一个值来表示函数是否成功
    的头像 发表于 10-31 14:43 484次阅读

    shell调用java并返回执行结果

    在Shell脚本中调用Java程序并获取执行结果,可以通过以下步骤实现: 编写Java程序:首先,你需要编写一个Java程序,包含你想要执行的功能。确保你的Java程序包含一个主类(包含main方法
    的头像 发表于 11-08 10:32 555次阅读

    tuple函数怎么返回多个值

    在编程领域中,函数是非常重要的构建模块,它能够接受输入参数并执行特定的计算,最终返回结果供程序使用。常规的函数只能返回一个值,但有时我们希望
    的头像 发表于 11-21 16:33 298次阅读

    回调函数(callback)是什么?回调函数的实现方法

    回调函数是一种特殊的函数,它作为参数传递给另一个函数,并在被调用函数执行
    发表于 03-12 11:46 306次阅读