«Когда-нибудь меняли значение 4?» - как это вошло в викторину Хейс-Томаса?

24

В 1989 году Феликс Ли, Джон Хейс и Анджела Томас написали тест Хакера, приняв форму викторины со многими инсайдерскими шутками: « Ты ешь слизь? »

Я рассматриваю следующие серии:

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

Есть ли конкретный анекдот, делающий цифру «4» определенной в серии?

Позволяет ли какая-то реализация на Фортране изменять значение констант? Было ли это возможно на других языках общего пользования в то время?

Михаэль Ле Барбье Грюневальд
источник
2
@ Возмутительно, я не возражаю, если мы оставим здесь второй вопрос, особенно если ответчики позаботятся объяснить, почему такое поведение существует в современных языках (т.е. есть ли практическое применение для него?). Тем не менее, это также сделало бы для отличного вопроса Code Golf .
Яннис
8
Связанный: Напишите программу, которая делает 2 + 2 = 5 . Java , и Python ответы там заменить 4на 5в интернированных списки целых чисел.
Мартин Питерс
5
И комментарий на этой странице гласит, что вы можете переопределить литералы в FORTRAN IV; 4 = 5было возможно
Мартин Питерс
7
И спасибо за тестовую ссылку Хакера. Теперь ты заставил меня чувствовать себя старым, а также в ужасе от того, как часто я мог отвечать «да» на вопросы.
Мартин Питерс
5
Я однажды изменил значение постоянного нуля в программе на фортране. Это было очень сложно исправить.
Брайан Оукли

Ответы:

32

В старые времена (1970-е и ранее) на некоторых компьютерах не было MMU (и это верно сегодня для очень дешевых микроконтроллеров).

В таких системах нет защиты памяти, поэтому в адресном пространстве нет сегмента, доступного только для чтения , и глючная программа может перезаписать константу (либо в памяти данных, либо даже внутри машинного кода).

Компиляторы Фортрана в то время передавали формальные аргументы по ссылке . Так что, если вы это сделали, CALL FUN(4)и SUBROUTINE FUN(I)его тело меняется I- например, с оператором I = I + 1в его теле, вы можете потерпеть катастрофу, сменив 4 на 5 в вызывающем абоненте (или еще хуже).

Это также справедливо для первых микрокомпьютеров, таких как оригинальный IBM PC AT 1984 года, с MS-DOS.

FWIW, я достаточно взрослый, чтобы использовать в начале 1970-х годов такие компьютеры: IBM1620 и CAB500 (в музее: это компьютеры эпохи 1960-х!). IBM1620 был довольно забавен: он использовался в таблицах памяти для сложений и умножений (и если вы перезаписали эти таблицы, произошел хаос). Таким образом, вы можете не только перезаписать 4, но вы можете даже перезаписать каждое последующее сложение 2 + 2 или умножение 7 * 8 (но я действительно забыл эти грязные детали, поэтому могу ошибаться).

Сегодня вы можете перезаписать код BIOS во флэш-памяти, если вы достаточно настойчивы. К сожалению, я больше не чувствую такого веселья, поэтому никогда не пытался. (Я даже боюсь установить LinuxBios на мою материнскую плату).

На современных компьютерах и в операционных системах передача константы по ссылке и ее изменение внутри вызываемого абонента просто вызовет нарушение сегментации , что звучит знакомо многим разработчикам на C или C ++.

Кстати: быть придирчивым: перезапись 4 - это не вопрос языка, а реализация.

Василий Старынкевич
источник
14
1620 получил прозвище CADET: не могу добавить, даже не пробую.
Пит Беккер
Трюк может быть почти повторен даже сейчас gfortran. Константы помещаются в их сегмент и передаются по ссылке на подпрограмму. По умолчанию постоянная секция доступна только для чтения, поэтому ошибка защиты памяти убивает программу.
Нетч
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

Boom! CALL SUBBAR(4)изменил значение 4 в литеральном пуле на 5. И тогда вам остается только удивляться, почему SUBBAZвы предполагаете, что вы передали ему 5 вместо того, что 4вы фактически написали в коде.

Более новые версии Fortran смягчают эту проблему, позволяя вам объявить INTENTпеременную как INили или OUTвыдавая ошибку (или, по крайней мере, предупреждение), если вы передаете константу в качестве OUTпараметра.

dan04
источник
5

В FORTRAN, когда константа передается другой процедуре, она больше не защищена. Вот на что они ссылаются. Другими популярными языками программирования в то время были C и Pascal, у которых не было (и до сих пор нет) этой проблемы. Возможно, есть более старые языки программирования, о которых я не знаю, которые имеют ту же проблему.

DJ Bazzie Wazzie
источник
Также это относится к тому факту, что пул констант не был в сегменте только для чтения. Если это произошло, и 4 передаются по ссылке, и изменяются вызываемым, SEGV будет происходить без успешно изменений 4.
Basile Starynkevitch
Это потому, что не каждая ОС имеет сегмент только для чтения. Подводный камень может быть использован, например, в DOS, операционные системы с сегментами только для чтения (использующими виртуальную память), такие как UNIX, будут возвращать ошибку ошибки сегментации во время выполнения. Во всяком случае, компилятор не должен допустить этого.
DJ Bazzie Wazzie
4
Я скучаю по Паскалю :(
Гарет
1
Чтобы быть более конкретным, FORTRAN проходит по ссылке. Поэтому, если вы передадите константу в качестве параметра функции, вы можете изменить это значение при каждом использовании этого числа.
Гейб
1
Только если эта константа (переданная по ссылке) остается в сегменте чтения-записи. Если он находится в .rodataсегменте только для чтения (как это делают текущие компиляторы), его изменение не изменит константу, а вызовет SEGV.
Василий Старынкевич,