「4の値を変更したことはありますか?」-これがどのようにHayes-Thomasクイズに入ったのですか?


24

1989年、リー・フェリックス、ジョン・ヘイズ、アンジェラ・トーマスは、多くのインサイダージョークを含むクイズ形式のハッカーのテストを書いた

次のシリーズを検討しています。

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

シリーズの中で「4」という数字を具体的にする特別な逸話はありますか?

一部のFortran実装では、定数の値を変更できましたか?これは、当時一般的に使用されていた他の言語でも可能でしたか?


2
@Ordousここで2番目の質問を続けても構いません。特に、このような動作が現代言語に存在する理由を説明者が説明する場合(つまり、実用的な用途はありますか)。それはそれは考え、言ったにも優れたために作るコードゴルフの質問。
ヤニス14

8
関連:2 + 2 = 5になるプログラムを作成します。JavaとPythonの回答は、インターンされた整数リストで置き換え4られ5ます。
マーティンピーターズ14

5
また、そのページのコメントには、FORTRAN IVでリテラルを再定義できると書かれています。4 = 5可能だった。
マーティンピーターズ14

7
そして、そのハッカーのテストリンクに感謝します。あなたは今、私に老いを感じさせ、質問に「はい」と答えることができる頻度に恐怖を感じさせました。
マーティンピーターズ

5
fortranプログラムで定数ゼロの値を一度変更しました。これは追跡が非常に難しいバグでした。
ブライアンオークリー14

回答:


32

昔(1970年代以前)には、一部のコンピューターにMMUがありませんでした(これは今日、非常に安価なマイクロコントローラーにも当てはまります)。

このようなシステムでは、メモリ保護がないため、アドレス空間に読み取り専用セグメントがなく、バグのあるプログラムが定数を上書きする可能性があります(データメモリ内、またはマシンコード内でも)。

当時のFortranコンパイラは、参照により形式引数を渡していました。そのため、たとえば体に変化がある場合CALL FUN(4)、たとえば体に声明があると、災害が発生し、呼び出し元で4が5に(またはさらに悪い)変化する可能性があります。SUBROUTINE FUN(I)II = I + 1

これは、MS-DOSを使用した1984年の最初のIBM PC ATのような最初のマイクロコンピューターにも当てはまりました。

FWIW、私は1970年代初頭の10代の高齢者として、IBM1620やCAB500(博物館では1960年代のコンピューターです!)を使ったことがあります。IBM1620は非常に楽しかったです。加算と乗算のためにメモリテーブルで使用されました(これらのテーブルを上書きすると、混乱が生じました)。したがって、4を上書きできるだけでなく、将来のすべての2 + 2加算または7 * 8乗算を上書きすることもできます(ただし、これらの詳細を本当に忘れてしまったため、間違っている可能性があります)。

今日、あなたが十分に頑張っているなら、フラッシュメモリのBIOSコードを上書きするかもしれません。悲しいことに、私はそれ以上の楽しみを感じていないので、試したことはありません。(私のマザーボードにいくつかのLinuxBiosをインストールすることさえ恐れています)。

現在のコンピューターおよびオペレーティングシステムでは、参照によって定数を渡し、呼び出し先内で定数を変更すると、多くのCまたはC ++開発者に馴染みのあるセグメンテーション違反が発生します。

ところで:ちょっとしたこと:上書き4は言語の問題ではなく、実装の問題です。


14
1620にはCADETという愛称が付けられました。追加することはできません。
ピートベッカー14

トリックは今でもほとんど繰り返すことができますgfortran。定数はセグメントに入れられ、サブルーチンへの参照によって渡されます。デフォルトでは、定数セクションは読み取り専用であるため、メモリ保護エラーによりプログラムが強制終了されます。
Netch 14

7

これは、誤ったコンパイラ最適化と組み合わせた、FORTRANの関数呼び出し評価戦略の意図しない副作用でした。

FORTRAN II は、参照渡しの引数持つユーザー定義関数とサブルーチンを導入しました。(なぜかわかりません。当時のIBMハードウェアでの値渡しよりもおそらく効率的でした。)

通常、参照渡しとは、r値の代わりにl値(変数など)を渡す必要があることを意味します。しかし、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

これは、すべてのOSに読み取り専用セグメントがあるわけではないためです。たとえば、DOSで落とし穴を使用すると、UNIXなどの読み取り専用セグメント(仮想メモリを使用)を備えたオペレーティングシステムは、実行時にセグメンテーションエラーを返します。とにかく、コンパイラはそれを許可すべきではありません。
dj bazzie wazzie 14

4
パスカルが恋しい:(
ガレス

1
具体的には、FORTRANは参照渡しです。したがって、関数パラメーターとして定数を渡すと、その数値を使用するたびにその値を変更できます。
ゲイブ14

1
その定数(参照によって渡される)が読み取り/書き込みセグメントにとどまっている場合のみ。.rodata(現在のコンパイラが行うように)読み取り専用セグメントにある場合、変更しても定数は変更されませんが、SEGVが発生します。
バジルStarynkevitch 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.