第四章 条条道路通罗马 ——《箴言》第四章 编程语言的运行机理之读书笔记

本文探讨了编程语言的发展历程,从早期的汇编语言到Fortran、BASIC,再到Pascal和C语言,强调了堆栈、函数和递归概念的演变。文章指出早期CPU并未内置堆栈,而随着技术进步,堆栈和递归运算得以实现。作者还澄清了关于C++中数据和代码分配的误解,并讨论了C++的面向对象特性,如类和继承。此外,文章讨论了C++中取函数地址的限制以及COM对象的实现。最后,文章提醒读者理解编程语言原理的重要性,以避免误解和混淆。
摘要由CSDN通过智能技术生成




“最早的CPU的体系结构中是没有堆栈的。CPU的处理过程……它是一种从头到尾的处理过程。从打孔机开始,到4位和8位的机器,甚至当时的巨型机也是这样,此时,一个程序就是一个过程,既没有堆栈也没有函数。最早的语言是汇编语言,后来产生了Fortran语言,Fortran语言之后又产生了BASIC语言。早期的语言都没有函数的概念,只有goto语句。”“在8位机的6502的CPU中,就出现了一点点的堆栈,前面有256个字节的堆栈区的空间,后面是主存。……这时候就出现了函数的概念,出现了GOSUB类语句的调用方法。早期堆栈主要是保存返回的地址,堆栈没有存放参数,6502就是这样的。这相当于可以调用较少的递归层次。这时候就引入了递归的概念,因为最开始程序并没有考虑到有这一种情况。当有了堆栈后,就能做递归,所以早期的BASIC是不能进行递归运算的,因为它没有堆栈区。……PC到了16位时代时,堆栈就不受限制了。堆栈是独立的,其空间也是不受限制的。此时,堆栈就可以存储参数。因为有了参数,所以可以做递归了,真正的递归程序才能运算。当时出现的Pascal语言,以至后来的C语言,都能进行递归程序的运算。”又在第159页里的插曲说,“最老的BASIC没有函数的概念,只能用goto语句,也没有return等类型的语句。后来出现了GOSUB等语句,像Fortran这样的语言一开始都是没有类似函数的语句。goto语句是不用堆栈的,自从有了堆栈以后才出现函数,只有有了函数后才会有全局、局部变量的出现。”

从这大段的文字可以看出,梁先生的意思是大抵明白的,就是,早期的CPU没有堆栈(作栈——Stack解,以下同),也就没有函数及其函数的概念,没有堆栈也就没有递归的概念和递归运算;直到8位机的6502这种CPU出现时才有了堆栈,才有了函数的概念和函数,才有GOSUB之类语句的调用方法,(16位PC时代)才有递归的概念和递归运算。而“当时出现的Pascal语言,以至后来的C语言,都能进行递归程序的运算。”

我们知道,6502这种CPU是1975年才出现的,不说别的单是就在它前头的Intel 8080就已经有了堆栈了,那时是1974年(1971年的Intel 4004就有三级深栈)了;而在这之前1970年代出来的小型机NEC PDP-11已经有堆栈了(参看《KA11 Processor Manual》Sep1970,有当前堆栈寄存器SP等,还有操作堆栈的指令如JSR、RTS、RTI、TRAP等)。【1961年 Burroughs 体系结构中设计有栈硬件。《操作系统——并发与分布式软件设计》附录A  (英国)Jean.Bacon 2002年】可见,出现堆栈的历史远在6502 CPU之前。而函数的概念更是老早就出现了,前面已经说过,远在Fortran出现之前的1948年就已经有子程序的概念了,而Fortran出现之初就有子程序和子程序库了(参看《Programmer’s reference manual FORTRAN》1956 Backus)。函数并不必然意味着堆栈【的使用】。照作者的说法,(1970年出现的)Pascal和(1972年代出现的)C语言就已经有递归了,而作者一面又说(直到1975年出现的)6502才有堆栈才有函数才有递归,这不是很矛盾吗?作者信口开河的程度不得不令人惊讶,就如同儿戏一般,还以此为我们打好基础、以便步入出神入化的编程高手境界呢。

如果没有弄错的话,BASIC在出现之初就已经有GOSUB语句(与RETURN语句结合使用)了。详情可以参看《A Manual for BASIC, the elementary algebraic language designed for use with the Dartmouth Time Sharing System》(1964年10月,The original Dartmouth BASIC manual,其中3.3 Functions and Subroutines)。

关于递归的概念和实现语言。Pascal语言1970年就出现了,远在6502CPU之前。其初就明确地谈到递归及其运算,在讲到函数时特地举了一些例子如GCD()函数:
function GCD(m,n:integer):integer;
begin if n = 0 then GCD := m else GCD := GCD(n,m mod n)
end
(《The Programming Language Pascal》1970 Wirth)。而1968年出版的《The Art of Computer Programming》(Knuth)就已经明确地讨论了递归及其运算【ALGOL60, 】。递归的实现同样也不必然同堆栈联系着。它还可以用寄存器、普通存储器等实现递归,不管这是否方便、是否有限制,但都能进行一定程度的递归。


“后来就引入了C++的概念。C++是这样一种工作方式,即把数据和对其操作的代码进行捆绑。这样就可以和结构一样,进行内存的分配和使用。在C++中,函数内部用动态的可重入的变量,而C++又是工作在自己独立的数据区和代码区。在C++在CPU中代码是共享的,数据是独立的,它们都是共享这个代码段。在编译器看来,代码是固定的。如果直接用指针的方法,在C++中取某个函数的地址,则不会成功。在这个类中的函数不会取出正确的地址。因为它生成时就是动态地分配一个空间。所以从语言的角度来说,让你不能取类成员函数的地址。但是C可以。因为定义C++的类时,代码和数据都浮动。然而,代码浮动是没有意义的。……对于同一个进程中,代码当然只有一份,因为代码是不会改变自己的。所以,在C++中加了不能取类函数地址的限制。有了这些限制,从C++的类中取一个函数的地址就会很困难。但还是可以做到的,在后面的C++语言特点中我们将会详细地介绍。但这时,C的方法可以取到函数的地址,地址是固定的。C++的设计理念是数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值