“是否更改4的值?”-Hayes-Thomas测验是如何进行的?


24

1989年,李·费利克斯(Felix Lee),约翰·海斯(John Hayes)和安吉拉·托马斯(Angela Thomas)进行了一次黑客测验,形式是带有许多内部笑话的测验,如:“ 你吃粘泥吗?

我正在考虑以下系列:

0015 Ever change the value of 4?
0016 ... Unintentionally?
0017 ... In a language other than Fortran?

在系列中是否存在使数字“ 4”特别的轶事?

是否有某些Fortran实现允许修改常量的值?当时可以用其他常用语言来实现吗?


2
@Ordous我不在乎我们是否在这里保留第二个问题,特别是如果回答者注意解释为什么现代语言中存在这种行为(即有实际用途吗?)。也就是说,这将成为一个很好的Code Golf问题。
扬尼斯2014年

8
相关:编写一个使2 + 2 = 5的程序。Java和Python的答案在那里替换45整数整数列表。
马丁·彼得

5
在该页面上注释指出,您可以在FORTRAN IV中重新定义文字。4 = 5是可能的。
马丁·皮耶特

7
并感谢您提供的Hacker测试链接。您现在使我感到老了,并对让我对问题回答“是”的频率感到恐惧。
马丁·皮耶特

5
我一次在fortran程序中更改了常数零的值。这是一个很难追踪的错误。
布莱恩·奥克利

Answers:


32

在过去(1970年代及以前),有些计算机没有任何MMU(今天,对于非常便宜的微控制器而言,确实如此)。

在这样的系统上,没有内存保护,因此地址空间中没有只读段,并且有缺陷的程序可能会覆盖常量(在数据内存中,甚至在机器代码内部)。

当时的Fortran编译器通过引用传递了形式参数。因此,如果您这样做CALL FUN(4)了,SUBROUTINE FUN(I)并且其主体发生了变化I-例如,主体中有一条语句I = I + 1,则可能会造成灾难,将调用方中的4变为5(或更糟)。

在最初的微型计算机(如1984年的原始IBM PC AT)和MS-DOS上也是如此

FWIW,我年纪大了,可以在1970年代初期使用这种计算机:IBM1620和CAB500(在博物馆中:这是1960年代的计算机!)。IBM1620非常有趣:它在内存表中用于加法和乘法(如果重写了这些表,则会造成混乱)。因此,不仅您可以覆盖4,而且甚至可以覆盖以后的每个2 + 2加法或7 * 8乘法(但是我真的忘记了这些脏的细节,所以可能是错误的)。

今天,如果您坚持不懈,可能会覆盖闪存中的BIOS代码。可悲的是,我再也感觉不到这种乐趣了,所以我从没有尝试过。(我什至担心在主板上安装一些LinuxBios)。

在当前的计算机和操作系统上,通过引用传递常量并在被调用者内部对其进行更改只会引起分段违规,这对许多C或C ++开发人员来说都是熟悉的。

顺便说一句:挑剔:覆盖4不是语言问题,而是实现的问题。


14
1620的绰号是CADET:无法添加,甚至无法尝试。
皮特·贝克尔

即使现在使用,该技巧也几乎可以重复gfortran。常量被放入其段中,并通过引用传递给子例程。默认情况下,常量部分是只读的,因此内存保护错误会杀死程序。
Netch

7

这是FORTRAN的函数调用评估策略与错误的编译器优化相结合的意外副作用。

FORTRAN II引入了用户定义的函数和子例程,其参数通过引用传递。(为什么,我不知道。这可能比当时的IBM硬件传值效率更高。)

通常,按引用传递意味着您必须传递l值(如变量)而不是r值。但是FORTRAN的设计师决定提供帮助,无论如何都可以让您将r值作为参数传递。编译器会自动为您生成一个变量。因此,如果您写道:

CALL SUBFOO(X + Y, 4)

编译器会将其转换为类似

TEMP1 = X + Y
TEMP2 = 4
CALL SUBFOO(TEMP1, TEMP2)

还有一种常见的编译器优化,称为“文字池”,可以将相同数字常量的多个实例合并到同一自动生成的变量中。(C家族中的几种语言都要求使用此字符串文字。)因此,如果您编写

CALL SUBBAR(4)
CALL SUBBAZ(4)

这将被视为

FOUR = 4
CALL SUBBAR(FOUR)
CALL SUBBAZ(FOUR)

在拥有一个可以更改其参数值的子程序之前,这似乎是一件非常合理的事情。

SUBROUTINE SUBBAR(X)
    !...lots of code...
    X = 5
    !...lots of code...
END SUBROUTINE SUBBAR

繁荣!CALL SUBBAR(4)将文字池中的4的值更改为5。然后您就纳闷为什么SUBBAZ要假设将其传递为5而不是4代码中实际编写的值。

较新版本的Fortran可以通过INTENT将变量的声明为IN或来缓解此问题,OUT如果您将常量作为OUT参数传递给您,则会出现错误(或至少是警告)。


5

在FORTRAN中,将常量传递给另一个过程时,该常量不再受保护。这就是他们所指的。在同一时间,其他流行的编程语言是C和Pascal,它们没有(并且仍然没有)出现此问题。也许有一些我不知道的较旧的编程语言也有同样的问题。


另外,它指的是常量池不在只读段中的事实。如果那样,和4通过引用传递,并通过改变被叫方,SEGV会发生没有成功改变4
巴西莱Starynkevitch

这是因为并非每个操作系统都有一个只读段。陷阱可用于DOS,例如,具有只读段(使用虚拟内存)的操作系统(如UNIX)将在运行时返回分段错误错误。无论如何,编译器不应允许它。
dj bazzie wazzie 2014年

4
我想念Pascal :(
Gareth 2014年

1
更具体地说,FORTRAN通过引用传递。因此,如果将常量作为函数参数传递,则可以在每次使用该数字时更改该值。
加布2014年

1
仅当该常量(通过引用传递)停留在读写段中时。如果它在.rodata只读段中(如当前的编译器所做的那样),则更改不会更改常量,但会导致SEGV。
Basile Starynkevitch 2014年
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.