人们谈论堆栈和堆是什么以及它们之间的差异.但我很想知道如果一个CPU不支持堆栈和堆结构,那么C可以在没有堆栈和堆的情况下正常运行吗?
不,不是的.让我们首先覆盖堆,这很容易.
NULL
每当您尝试调用malloc
(或任何其他内存分配函数)时,不提供任何排的堆的实现只需要返回.根据标准,这是完全可以接受的行为.
就堆栈而言,它也不需要提供一个.ISO C11提到"堆栈"这个词恰好是零次.
什么实现它需要做的仅仅是一个正确的"虚拟机"的所有标准中规定的事情.如果没有筹码,这将是非常困难的,但这并非不可能.作为一种极端情况,没有任何内容表明您不能简单地以递归方式内联每个函数调用.这将使用相当多的代码和特定于功能的数据空间,但它肯定是可行的.
然而,它可能会说服我转移到另一个架构,一个确实有堆栈(和堆,就此而言).
话虽如此,即使架构既不提供堆也不提供堆栈,这两者都可以基本的内存I/O操作构建.事实上,我作为青少年时曾经拥有的最早的计算机之一就是没有专用堆栈的RCA 1802 CPU .它甚至没有call
或ret
指示.
然而,它可以使用其SCRT(标准调用和返回技术)很好地处理子程序和堆栈(对于"well"这个词的某些定义).请参阅此处,了解更多有关美容(或怪物,取决于您的观点)如何工作的细节,以及其他一些不寻常的架构.
IBM Z(又名System z,zSeries,无论他们本周在调用它们)实际上都有一堆(各种各样的,你可以从操作系统中分配内存)但没有堆栈.它实际上通过使用此堆内存以及某些寄存器(类似于上面链接中引用的RCA芯片)来实现链接列表堆栈,这意味着函数prolog使用本地函数内存STORAGE OBTAIN
并且epilog将其释放STORAGE RELEASE
.
不用说,在每个函数的prolog和epilog中添加了相当多的额外代码.
C11标准不需要堆栈或堆栈(参见n1570),正如其他答案所解释的那样.但是,在实践中,两者都很有用.
关于调用堆栈,它很常见,但它可能是"避免":
一些优化编译器可能足够聪明(特别是整个程序优化或链接时优化)来检测整个程序不需要任何东西的情况(一个简单的例子就是没有函数指针且没有递归的整个程序) :在这种情况下,每个"调用帧"都可以在编译时静态分配.许多优化编译器正在内联一些调用(有效地消除了对这些调用的调用堆栈的需要),即使对于未标记的函数也是如此inline
.
许多今天的处理器(x86或x86-64,AVR,SPARC,ColdFire,即mc68K ......)都有一个硬件调用堆栈(例如一些" 堆栈指针 " 寄存器,已知为函数调用).在没有这种硬件堆栈指针的那些(例如IBM Z系列大型机,PowerPC,MIPS,RISC-V或ARM),调用约定(或ABI)可能通常会使用一些寄存器来发挥作用堆栈指针.BTW,C甚至可以在理论上实现随机访问机器(没有任何寄存器或堆栈指针).
可以想象一个编译器使用延续传递样式来避免调用堆栈(有效地,在某些堆中的-or中"分配"一些"调用帧",可能在某些垃圾收集器的帮助下).Appel的旧书Compiling with Continuations详细解释了这个想法.我无法命名任何C编译器这样做,但标准允许这样的方法.如果您将一些编译器从C编译为SML(或某些Lisp),您可以使用这样的编译器.
关于C堆,该malloc
函数可能总是失败,并且仍然是标准兼容的.我声称这样的malloc
无用,但可能是最快的.此外,C标准允许独立实现完全没有任何实现malloc
.
如果您寻求C几乎要求的功能,我会研究二进制表示.我倾向于认为为十进制计算机(如旧的IBM 1620)或三元计算机(如Setun)实现C11编译器是非常不切实际的(但原则上它是可能的,因为你可以" 模拟 "二进制计算机三元或十进制;都是图灵完备的).然而,你会发现只有博物馆这么古老的(20世纪50年代末,20世纪60年代早期)的计算机,它们在C发明之前就消失了.
BTW,调用堆栈和堆栈存在于ALGOL和Lisp(和IPL-V)实现中,比C早几十年.
归档时间: |
|
查看次数: |
310 次 |
最近记录: |