不使用某种堆栈就无法实现函数调用语义。只能玩文字游戏(例如,使用其他名称,例如“ FILO返回缓冲区”)。
可以使用一些没有实现函数调用语义的东西(例如,连续传递样式,参与者),然后在其之上构建函数调用语义。但这意味着添加某种数据结构以跟踪函数返回时将控制权传递到何处,并且该数据结构将是一种堆栈类型(或名称/描述不同的堆栈)。
假设您有许多可以互相调用的函数。在运行时,每个函数都必须知道该函数退出时要返回的位置。如果有first
电话,second
您将:
second returns to somewhere in first
然后,如果有second
电话,third
您将:
third returns to somewhere in second
second returns to somewhere in first
然后,如果有third
电话,fourth
您将:
fourth returns to somewhere in third
third returns to somewhere in second
second returns to somewhere in first
调用每个函数时,必须将更多的“返回位置”信息存储在某个位置。
如果函数返回,那么将使用并且不再需要其“返回位置”信息。例如,如果fourth
返回到某处,third
则“返回到何处”信息的数量将变为:
third returns to somewhere in second
second returns to somewhere in first
基本上; “函数调用语义”意味着:
- 您必须具有“返回地点”信息
- 信息量随着函数的调用而增加,并在函数返回时减少
- 存储的第一条“返回位置”信息将是丢弃的最后一条“返回位置”信息
这描述了FILO / LIFO缓冲区或堆栈。
如果尝试使用一种树,则树中的每个节点将永远不会有多个子节点。注:如果一个函数调用2层或更多的功能与多个孩子的节点只能发生在同一时间,这需要某种并发性(如螺纹,叉()等),它不会是“函数调用的语义”。如果树中的每个节点永远都不会超过一个子节点;那么该“树”将仅用作FILO / LIFO缓冲区或堆栈;并且由于它仅用作FILO / LIFO缓冲区或堆栈,因此可以断言“树”是堆栈(唯一的区别是文字游戏和/或实现细节)。
这同样适用于可以想象用来实现“函数调用语义”的任何其他数据结构-它将用作堆栈(唯一的区别是文字游戏和/或实现细节);除非它破坏了“函数调用语义”。注意:如果可以的话,我将提供其他数据结构的示例,但我认为没有任何其他合理的结构。
当然,如何实现堆栈是实现细节。它可能是一个内存区域(您在其中跟踪“当前堆栈顶部”),它可能是某种类型的链接列表(在此您跟踪“列表中的当前条目”),或者可以在某些区域中实现另一种方式。硬件是否具有内置支持也无关紧要。
注意:在任何时候,如果只有一个过程的一次调用处于活动状态;那么您可以为“返回地点”信息静态分配空间。这仍然是一个堆栈(例如,以FILO / LIFO方式使用的静态分配条目的链表)。
还要注意,有些事情不遵循“函数调用语义”。这些内容包括“可能非常不同的语义”(例如,连续传递,参与者模型);并且还包括对“函数调用语义”的通用扩展,例如并发(线程,纤维等),setjmp
/ longjmp
,异常处理等。