Лекции по построению компилятора на Pascal
как мы в любом случае передавали адрес, казалось имелся большой смысл в том, чтобы
просто передавать адрес целочисленного литерала, 4 в примере выше.
Чтобы сделать вопрос более интересным большинство компиляторов тогда и сейчас
идентифицирует все литералы] и сохраняет их отдельно в "литерном пуле", так что мы
должны сохранять только одно значение для каждого уникального литерала. Такая
комбинация проектных решений: передача выражений, оптимизация литералов как
специальных случаев и использование литерного пула - это то, что вело к бедствию.
Чтобы увидеть, как это работает, вообразите, что мы вызываем подпрограмму FOO как в
примере выше, передавая ей литерал 4. Фактически, что передается - это адрес литерала 4,
который сохранен в литерном пуле. Этот адрес соответствует формальному параметру K в
самой подпрограмме.
Теперь предположите, что без ведома программиста подпрограмма FOO фактически
присваивает K значение -7. Неожиданно, литерал 4 в литерном пуле меняется на -7. В
дальнейшем, каждое выражение, использующее 4, и каждая подпрограмма, в которую
передают 4, будут использовать вместо этого значение -7! Само собой разумеется, что это
может привести к несколько причудливому и труднообъяснимому поведению. Все это дало
концепции передачи по ссылке плохое имя, хотя, как мы видели, в действительности это
была комбинация проектных решений, ведущая к проблеме.
Несмотря на проблему, подход Фортрана имел свои положительные моменты. Главный из
них - тот факт, что мы не должны поддерживать множество механизмов. Та же самая схема
передачи адреса аргумента работает для всех случаев, включая массивы. Так что размер
компилятора может быть сокращен.
Частично из-за этого подводного камня Фортрана и частично просто из-за уменьшенной
связи, современные языки типа C, Pascal, Ada и Modula 2 в основном передают скаляры по
значению.
Это означает, что значение скаляра копируется в отдельное значение, используемое только
для вызова. Так как передаваемое значение - копия, вызываемая процедура может
использовать его как локальную переменную и изменять ее любым способом, каким
нравится. Значение в вызывающей программе не будет изменено.
Сначала может показаться, что это немного неэффективно из-за необходимости копировать
параметр. Но запомните, что мы в любом случае окажемся перед необходимостью выбирать
какое-то значение, является ли оно непосредственно параметром или его адресом. Внутри
подпрограммы, использование передачи по значению определенно более эффективно, так
как мы устраняем один уровень косвенности. Наконец, мы видели раньше, что в Фортране
часто было необходимо в любом случае делать копии внутри подпрограммы, так что
передача по значению уменьшает количество локальных переменных. В целом, передача по
значению лучше.
Исключая одну маленькую деталь: если все параметры передаются по значению, у
вызванной процедуры нет никакого способа возвратить результат в вызвавшую!]]]]
Переданный параметр не изменяется в вызвавшей подпрограмме а только в вызванной. Ясно,
что так работы не сделать.
Существуют два эквивалентных ответа на эту проблему. В Паскале Вирт предусмотрел]
параметры-переменные, которые передаются по ссылке. VAR параметр не что иное как наш
старый друг параметр Фортрана с новым именем и расцветкой для маскировки. Вирт
аккуратно обходит проблему "изменения литерала" так же как проблему "адрес выражения"
с помощью простого средства, разрешая использовать в качестве фактических параметров
только переменные. Другими словами, это тоже самое ограничение, которое накладывали
самые ранние версии Фортрана.
Си делает ту же самую вещь, но явно. В C все параметры передаются по значению. Однако
одним из видов переменных, которые поддерживает С, является указатель. Так передавая