什么时候以及为什么指针开始被视为冒险?


18

似乎在考虑在编程语言中使用指针时,已经有了逐渐的转变,因此,人们普遍认为指针被认为是有风险的(如果不是彻头彻尾的“邪恶”或类似的强化)。

这种思想转变的历史发展是什么?是否有特定的开创性事件,研究或其他发展?

例如,简单回顾一下从C到C ++到Java的过渡,似乎显示出一种补充指针,然后完全用引用替换指针的趋势。但是,真正的事件链可能比这更微妙和复杂,并且不是那么连续。使其成为那些主流语言的功能可能起源于其他地方,也许很久以前了。

注意:我并不是在问指针,引用和其他东西的实际优点。我的重点是这种明显转变的理由。


1
这是由于人文教育的下降。人们不再能够理解所有CPU中都包含的间接引用,这是计算技术中最基本的思想之一。

10
指针风险。您为什么认为思想发生了转变?语言功能和硬件已得到改进,使编写软件时无需使用指针,尽管不会造成性能损失。
停止伤害莫妮卡

4
@DaveInCaz据我所知,特定的开发是指针的发明。
停止伤害Monica

5
@nocomprende:您刚刚写的不是事实,证据也不是意见。1970年程序员的数量要少得多,您没有任何证据可以证明当今使用“间接引用”的人数是好是坏。
whatsisname

3
从第一天开始,指针一直被认为是有风险的。将它们从汇编语言转移到高级语言只是一种折衷。
Frank Hileman

Answers:


21

基本原理是开发指针的替代方法。

在后台,任何指针/引用/等都被实现为包含内存地址(即指针)的整数。当C出现时,此功能作为指针公开。这意味着底层硬件可以处理内存的任何事情都可以使用指针来完成。

这始终是“危险的”,但危险是相对的。在编写1000行程序时,或者在执行IBM级软件质量程序时,可以很容易地解决这种危险。但是,并非所有软件都以这种方式开发。因此,出现了对更简单结构的需求。

如果您考虑一下,an int&和a int* const确实具有相同的安全级别,但是一个具有比另一个更好的语法。 int&也可能会更有效,因为它可以引用存储在寄存器中的int(过时:这在过去是正确的,但是现代编译器非常擅长优化,只要在寄存器中有指向整数的指针,就可以您永远不会使用任何需要实际地址的功能,例如++

当我们转向Java时,我们转向提供一些安全性保证的语言。 CC ++没有提供。Java保证仅执行合法操作。为此,java完全放弃了指针。他们发现,在真实代码中完成的绝大多数指针/引用操作都是引用所能满足的。仅在少数情况下(例如,通过数组快速迭代)才真正需要指针。在这些情况下,java会对运行时造成不利影响,以避免使用它们。

此举并非单调。 C#重新引入了指针,尽管形式非常有限。它们被标记为“ unsafe ”,这意味着它们不能被不受信任的代码使用。它们还具有关于可以指向和不能指向的内容的明确规则(例如,将指针增加到数组末尾只是无效的)。但是,他们发现在少数情况下需要高性能的指针,因此他们将它们放回去。

同样有趣的是函数式语言,它们根本没有这样的概念,但这是一个完全不同的讨论。


3
我不确定说Java没有指针是正确的。我不想就什么是指针和不是指针进行深入的辩论,但是JLS说“引用的值是指针”。不允许直接访问或修改指针。这也不只是为了安全,让人们脱离随时跟踪对象位置的业务对GC很有帮助。
JimmyJames

6
@JimmyJames是的。出于此答案的目的,指针与非指针之间的分界线是它是否支持指针算术运算,引用通常不支持该运算。
Cort Ammon-恢复莫妮卡

8
@JimmyJames我同意Cort的断言,即指针是可以进行算术运算的东西,而引用则不能。用Java之类的语言实现引用的实际机制是实现细节。
罗伯特·哈维

3
通常,C和C ++通过允许在规范中加入许多“未定义的行为”,自愿接受了这个危险俱乐部的成员资格。
rwong

2
顺便说一下,有一些 CPU,它们可以区分指针和数字。例如,IBM AS / 400中的原始48位CISC CPU就是这样做的。实际上,在操作系统下方有一个抽象层,这意味着CPU不仅在数字和指针之间进行区分,并且禁止对指针进行算术运算,而且操作系统本身甚至根本不了解指针语言也不知道。有趣的是,这使原始的AS / 400成为一个系统,在该系统中,用C用高级脚本语言重写代码使其速度降低了几个数量级。
约尔格W¯¯米塔格

10

对于复杂的程序(例如,递归或可变大小的数据结构),某种间接是必需的。但是,没有必要通过指针实现此间接。

大多数高级编程语言(即非Assembly)都是相当内存安全的,并且不允许无限制的指针访问。C族在这里很奇怪。

C是从B演变而来的,这是对原始程序集的非常抽象的抽象。B有一个类型:单词。该词可以用作整数或指针。当整个内存被视为单个连续数组时,这两个等效。C保留了这种相当灵活的方法,并继续支持固有的不安全指针算法。C的整个类型系统更像是事后的想法。这种对内存访问的灵活性使C非常适合其主要用途:对Unix操作系统进行原型设计。当然,事实证明Unix和C非常流行,因此在实际上不需要这种低级内存方法的应用程序中也使用C。

如果我们看一下C之前的编程语言(例如Fortran,Algol方言,包括Pascal,Cobol,Lisp等),其中某些确实支持C指针。值得注意的是,空指针概念是1965年为Algol W发明的。但是,这些语言都没有尝试成为类似C的高效低抽象系统语言:Fortran是用于科学计算的,Algol开发了一些相当高级的概念,Lisp是与行业级语言相比,它更多的是研究项目,而Cobol则专注于业务应用程序。

垃圾收集自50年代末开始存在,即早于C(70年代初)。GC需要内存安全才能正常工作。C之前和之后的语言均使用GC作为正常功能。当然,这会使语言变得复杂得多,甚至可能变慢,这在大型机时代尤其明显。GC语言倾向于以研究为导向(例如Lisp,Simula,ML)和/或需要功能强大的工作站(例如Smalltalk)。

随着更小,功能更强大的计算机在通用语言和GC语言中的特殊应用,确实​​变得越来越流行。对于非实时应用程序(有时甚至是那时),GC现在是首选方法。但是GC算法也一直是深入研究的主题。作为替代方案,在没有GC的情况下,还进一步开发了更好的内存安全性,尤其是在最近的三十年中:值得注意的创新是RAII和C ++中的智能指针以及Rust的生命周期系统/借用检查器。

Java并不是通过内存安全的编程语言进行创新的:它基本上采用了GCed的语义,内存安全的Smalltalk语言,并将它们与C ++的语法和静态类型结合在一起。然后将其作为更好,更简单的C / C ++进行销售。但这只是表面上的C ++后代。Java缺少指针的原因更多是因为Smalltalk对象模型而不是C ++数据模型被拒绝。

因此,不应将Java,Ruby和C#等“现代”语言解释为克服C语言中的原始指针问题,而应将其视为许多传统的借鉴,包括C语言,还应借鉴更安全的语言,例如Smalltalk,Simula,或Lisp。


4

根据我的经验,对于许多人而言,指针始终是一个具有挑战性的概念。1970年,我就读的大学拥有Burroughs B5500,我们在编程项目中使用了Extended Algol。硬件体系结构基于描述符和数据字上部的一些代码。这些被明确设计为允许数组使用指针而不会被允许走到最后。

我们在课堂上进行了热烈的讨论,涉及名称与价值参考以及B5500阵列如何工作。我们中有些人立即得到了解释。其他人没有。

后来,硬件未能保护我免受失控的指针(特别是汇编语言)的影响,这让我有些震惊。毕业后的第一份工作中,我帮助解决了操作系统中的问题。通常,我们仅有的文档是打印的故障转储。我开发了一种在内存转储中查找失控指针的来源的诀窍,因此每个人都给我“不可能的”转储以弄清楚。我们遇到的更多问题是指针错误引起的,而不是其他类型的错误。

与我合作的许多人开始编写FORTRAN,然后转到C,编写了很像FORTRAN的C,并且避免使用指针。由于Java从不内部化指针和引用,因此会带来问题。通常,对于FORTRAN程序员而言,了解对象分配的真正工作原理是一项挑战。

现代语言使在“幕后”需要指针的事情变得更加容易,同时又使我们免受拼写错误和其他错误的影响。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.