“Pernah mengubah nilai 4?” - bagaimana ini muncul dalam kuis Hayes-Thomas?

24

Pada tahun 1989 Felix Lee, John Hayes dan Angela Thomas menulis tes Hacker berupa kuis dengan banyak lelucon orang dalam, seperti, “ Apakah Anda makan jamur lendir?

Saya mempertimbangkan seri berikut:

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

Apakah ada anekdot khusus yang membuat angka "4" khusus dalam seri?

Apakah beberapa implementasi Fortran memungkinkan untuk mengubah nilai konstanta? Apakah ini mungkin dalam bahasa lain yang umum digunakan pada saat itu?

Michael Le Barbier Grünewald
sumber
2
@Ordous Saya tidak keberatan jika kita menyimpan pertanyaan kedua di sini, terutama jika penjawab berhati-hati untuk menjelaskan mengapa perilaku seperti itu ada dalam bahasa modern (yaitu apakah ada kegunaan praktis untuk itu?). Yang mengatakan, itu juga akan membuat pertanyaan Golf Code yang sangat baik .
yannis
8
Terkait: Tulis program yang menghasilkan 2 + 2 = 5 . Java dan Python menjawab di sana menggantikan 4untuk 5dalam daftar bilangan bulat diinternir.
Martijn Pieters
5
Dan komentar pada halaman tersebut menyatakan bahwa Anda dapat mendefinisikan kembali literal dalam FORTRAN IV; 4 = 5itu mungkin.
Martijn Pieters
7
Dan terima kasih atas tautan tes Hacker itu. Anda sekarang membuat saya merasa tua, serta ngeri tentang seberapa sering saya bisa menjawab 'ya' untuk pertanyaan.
Martijn Pieters
5
Saya mengubah nilai nol konstan dalam program fortran sekali. Itu adalah bug yang sangat sulit untuk dilacak.
Bryan Oakley

Jawaban:

32

Di masa lalu (1970-an dan sebelumnya) beberapa komputer tidak memiliki MMU (dan ini berlaku hari ini untuk mikrokontroler yang sangat murah).

Pada sistem seperti itu, tidak ada perlindungan memori sehingga tidak ada segmen baca-saja di ruang alamat , dan program kereta dapat menimpa konstanta (baik dalam memori data, atau bahkan di dalam kode mesin).

Para penyusun Fortran pada waktu itu mengeluarkan argumen formal dengan referensi . Jadi jika Anda melakukannya CALL FUN(4)dan SUBROUTINE FUN(I)tubuhnya berubah I- misalnya dengan pernyataan I = I + 1di tubuhnya, Anda dapat mengalami bencana, mengubah 4 menjadi 5 di pemanggil (atau lebih buruk).

Ini juga berlaku pada mikrokomputer pertama seperti IBM PC AT asli dari tahun 1984, dengan MS-DOS

FWIW, saya sudah cukup tua untuk digunakan, sebagai remaja belasan pada awal 1970-an, komputer seperti: IBM1620 dan CAB500 (di museum: ini adalah komputer era 1960-an!). IBM1620 cukup menyenangkan: digunakan dalam tabel memori untuk penambahan dan perkalian (dan jika Anda menimpa tabel ini, kekacauan terjadi). Jadi tidak hanya Anda bisa menimpa 4, tetapi Anda bahkan bisa menimpa setiap penambahan 2 + 2 atau 7 * 8 perkalian di masa depan (tapi saya benar-benar lupa detail kotor ini sehingga bisa salah).

Hari ini, Anda mungkin menimpa kode BIOS dalam memori flash, jika Anda cukup gigih. Sedihnya, saya tidak merasakan kesenangan itu lagi, jadi saya tidak pernah mencoba. (Saya bahkan takut menginstal beberapa LinuxBios di motherboard saya).

Pada komputer saat ini dan sistem operasi yang melewati konstanta dengan referensi dan mengubahnya di dalam callee hanya akan memicu pelanggaran segmentasi , yang terdengar akrab bagi banyak pengembang C atau C ++.

BTW: menjadi nitpicking: menimpa 4 bukan masalah bahasa, tetapi implementasi.

Basile Starynkevitch
sumber
14
1620 dijuluki CADET: Tidak Dapat Menambahkan, Bahkan Tidak Mencoba.
Pete Becker
Caranya hampir bisa diulang bahkan sekarang dengan gfortran. Konstanta dimasukkan ke dalam segmen mereka dan diteruskan dengan referensi ke subrutin. Secara default, bagian konstan hanya-baca, sehingga kesalahan perlindungan memori membunuh program.
Netch
7

Itu adalah efek samping yang tidak disengaja dari strategi evaluasi panggilan fungsi FORTRAN dalam kombinasi dengan optimasi kompiler yang salah.

FORTRAN II memperkenalkan fungsi dan subrutin yang ditentukan pengguna dengan argumennya yang disahkan oleh referensi . (Ya, saya tidak tahu. Mungkin itu lebih efisien daripada nilai kelulusan pada perangkat keras IBM saat itu.)

Biasanya, pass-by-reference berarti Anda harus melewati nilai-l (seperti variabel) alih-alih nilai-r. Tetapi para perancang FORTRAN memutuskan untuk membantu dan membiarkan Anda memberikan nilai-r sebagai argumen. Kompiler akan secara otomatis menghasilkan variabel untuk Anda. Jadi, jika Anda menulis:

CALL SUBFOO(X + Y, 4)

kompiler akan mengonversi ini di belakang layar menjadi sesuatu seperti

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

Ada juga optimasi kompiler umum yang disebut "kumpulan literal", yang akan mengkonsolidasikan beberapa contoh dari konstanta numerik yang sama ke dalam variabel yang dihasilkan secara otomatis yang sama. (Beberapa bahasa dalam keluarga C membutuhkan ini untuk string literal.) Jadi, jika Anda menulis

CALL SUBBAR(4)
CALL SUBBAZ(4)

ini akan diperlakukan seolah-olah demikian

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

yang tampaknya seperti hal yang masuk akal untuk dilakukan sampai Anda memiliki subprogram yang mengubah nilai parameternya.

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

Ledakan! CALL SUBBAR(4)mengubah nilai 4 dalam kumpulan literal ke 5. Dan kemudian Anda bertanya-tanya mengapa SUBBAZdengan asumsi Anda memberikannya 5 bukannya 4Anda benar-benar menulis dalam kode.

Versi yang lebih baru dari Fortran mengurangi masalah ini dengan membiarkan Anda mendeklarasikan INTENTvariabel sebagai INatau OUT, dan memberi Anda kesalahan (atau setidaknya peringatan) jika Anda memberikan konstanta sebagai OUTparameter.

dan04
sumber
5

Dalam FORTRAN, ketika sebuah konstanta dilewatkan ke prosedur lain, itu tidak lagi dilindungi. Itulah yang mereka rujuk. Bahasa pemrograman populer lainnya dalam waktu yang sama adalah C dan Pascal yang tidak (dan masih belum) memiliki masalah ini. Mungkin ada bahasa pemrograman lama yang tidak saya sadari memiliki masalah yang sama.

dj bazzie wazzie
sumber
Juga, ini merujuk pada fakta bahwa kumpulan konstan tidak dalam segmen hanya baca. Jika ya, dan 4 dilewatkan dengan referensi, dan diubah oleh callee, SEGV akan terjadi tanpa berhasil mengubah 4.
Basile Starynkevitch
Itu karena tidak semua OS memiliki segmen read-only. Perangkap itu dapat digunakan pada DOS misalnya, sistem operasi dengan segmen hanya-baca (menggunakan memori virtual) seperti UNIX akan mengembalikan kesalahan kesalahan segmentasi pada waktu berjalan. Bagaimanapun, kompiler seharusnya tidak mengizinkannya.
dj bazzie wazzie
4
Saya rindu Pascal :(
Gareth
1
Untuk lebih spesifik, FORTRAN melewati referensi. Jadi, jika Anda memberikan konstanta sebagai parameter fungsi, Anda bisa mengubah nilai itu untuk setiap penggunaan angka itu.
Gabe
1
Hanya jika konstanta itu (lewat referensi) tetap berada di segmen baca-tulis. Jika berada dalam .rodatasegmen read-only (seperti yang dilakukan oleh kompiler saat ini) mengubahnya tidak akan mengubah konstanta tetapi akan menyebabkan SEGV.
Basile Starynkevitch