Т. Пратт, М. Зелковиц - Языки программирования - разработка и реализация (4-е издание_ 2002) (1160801), страница 119
Текст из файла (страница 119)
а яв ьс [в.' ), Гог [и -- ); сн=з вв) ргтыд "$ОЗп".с[в)), ) Какие значения теперь будут р кпсчатаны при выполнении р? Отметим, что начальное значение а равно 2; но поскольку этот параметр передается по ссылке, то при выполнении [( сто значение поменялось на 3, прежде чем с[а) увеличилось на 1 через указатель, хранящийся в 3, 11о тогда добавит ли оператор *) = *1 к 1 единицу к с[21 или к с[317 Очевидно, что зто будет компонент с[21, а пе с[31, так как выражение фактического параметра с[в1, определяющее значение указателя на компонент массива с, вычисляется в точке вызова подпрограммы й.
Но во время 430 Глава 9. управление подпрограммами вызова 8 значение е было равно 2, поэтому подпро1 рамме 8 передается указатель на второй элемент массива с. Более того, подпрограмме 8 вообще ничего не известно о существовании с(31, так как внутри 8 указатель на с(21 воспринимается так же, как указатель на любой объект данных простой целой переменной.
Таким образом, будут напечатаны следующие значения; 8 8 8 8 8. (Если бы параметры передавались по имени, был бы другой эффект,) Указатели. Предположим, что фактический параметр является простой переменной типа укдзааель или структурой данных типа массива или записи, компонентами которой являются йзпач ения (указатели на объекты данных), Например, предположим, что в подпрограмме Р имя х объявлено как чесС *х н соответствующий формальный параметр в 0 объявлен аналогичным образом: чесс *й. Независимо от того, объявлен ли параметр й как передаваемый по ссылке илп по значению, эффект от передачи х в качестве фактического параметра заключается в том, что подпрограмма 0 получает прямой доступ к вектору, на который указывает х.
Если передача осуществляется по значению, то 0 имеет собственную копию Рзпачения, которая содержит х, так что и х, и н указывают на один и тот же вектор. Если передача происходит по ссылке, то й содержит указатель на х, который, в свою очередь, содержит указатель на вектор.
Можно сформулировать общее правило: если фактический параметр содержит указатель или компоненты, содержащие указатели, то объекты данных, на которые ссылаются эти указатели, непосредственно доступны из вызываемой подпрограммы, независимо от метода передачи параметров. Отметим, что если в качестве параметра подпрограмме передается до значению связанный список (нли какая-нибудь другая связанная структура данных), то обычно это означает, что в подпрограмму во время передачи копируется только указатель на первый элемент; вся связанная структура не копируется.
Этим свойством переменных-указателей и добавлением различных операций с г- и йзначениями в С объясняется то, чзо в языке С не требуется наличия явного механизма передачи параметров по ссь~лке, а достаточно передачи но значсшпо. Результаты выражений. Если мы хо гим передать по ссылке какое-либо выражение, например а ь Ь, в нодпрограмму 0($(а + ОЕьэй то транслятор лолжец вычислить зпачснпс выражения в точке вызова, записать это значение во временную облисть пиилти в Р и передать указатель па эту область в подпрограмму 0 как параметр. Подпрограмма 0 выполняется так жс, как и раньше.
Передача по ссылке приводит к тому, что формальный параметр содержит указатель на временную область памяти в Р. Поскольку эта область не имеет никакого имени, по которому на нес можно было бы ссылаться в Р, то любые присваивания ей новых значений в полпрограмме 0 не изменякл значение, на которос Р впоследствии может сослаться. Таким образом, подпрограмма 0 не может вернуть результат в Р через параметр, передаваемый по ссылке. И в 1'азса1, и в С этот случай, когда передача является передачей по ссылке, запрещен, поскольку присваивания формальному параметру не вызывают видимого эффекта в вызывающей подпрограмме (и ~ юзтому в таких случаях следует использовать передачу по значению).
Псевдонимы и параметры Возможность использования псевдонимов (нескольких имен для олного и того же объекта данных в одной среде ссылок) в большинстве языков программирования 9.3. Передача параметров 43 с возникает в связи с передачей параметров, Как было показано в разделе 9.2.1, ис- пользование псевдонимов вызывает проблемы в понимании и проверке правиль- ности программ, а также затрудняет оптимизацию.
Псевдоним может быть создан в процессе передачи параметров одним из двух способов, 1. Формальный пири метр и нелокильная переменпая. Подпрограмма может иметь прямой доступ к объекту данных, переданному как фактический параметр по ссылке, через нелокальнос имя. Тогда имя формшсьпого параметра и нелокальное имя становятся псевдонимами, так как каждое из них ссылается на один и тот жс объект данных, В листинге 9.2 приведен пример такого способа создания псевдонимов.
2. 21еи форлсальннх пиралсетра. Один и тот жс объект данных может быть пере)Сан в качестве фактического параметра по ссылке в двух позициях одного и того же списка фактических параметров. Тогда два имени соответствующих формальных параметров становятся псевдонимами, так как любое из них может быть использовано для ссьслкн на исходи ый объект данных. Например, процедура )С, определенная в языке Раэса) при помощи следующей спецификации; ргосеииге Шхаг с,) сэсеиег), может быль вызвана из Р таким образом; к1сэ,сз).
Вовремя выполнения к и переменная ц и переменная ) содержат указатели на один и тот же объект ланных эс в г, поэтому с и ) являются псевдонимами. В языке ГОКТГсАсч'такой способ создания псевдонимов запрещен. Подпрограммы как параметры Во многих языках подпрограмма может быть передана другой подпрограмме в качсствс фактического параметра. В этом случае выражение фактического параметра состоит нз илсесси псрелавасмой подпрограммы. Тогда соответствующий формальный параметр определяется как ссаралсетр, тин которого -- подпрограмма. Например, в языке Разов! можно опрслелить подпрограмму 0 с формальным параметром СС, тин которого — процедура или функция: ргосес)иге 0Сх. спгеэег.
1эпссиьп й(у,г: сптеэег). сшеэег), Тогда 0 можно вызывать, передавая сй в качестве второго параметра полпрограммуфункцию(ссассрссхсср, вызов 012!, 1п) вызывает подпрограмму 0 и передает подпрограмму-функцию 1п в качестве параметра), Внутри 0 подпрограмма, передашщя как параметр, может Г>ьпь вызвана с использованием имени формального параметра к. Например, г;= к1 с, х) вызывает подпрограмму, являющуюся фактическим параметром 1фусскция 1п в предшествующем вызове), в которую передасотся фактические ссараметрьс с и х. Таким образом, в дассссом сссучае вызов)С)с, х) эквивалентен вызову 1п1с х). Голи при слсдуннцсм обращении к подпрограмме 0 ей будет передан другой фактический параметр-подпрограмма, например функция 1п2, то хсс, х) будет эквивалентно 1п2Н.
х), С передачей подпрограмм в качестве параметров связаны две основные проблемы. Статическая проеерки типот Когда подпрограмма, переданная как параметр в другусо подпрограмму, вызывается с использованием имени формального пара- 432 Глава 9. Управление подпрограммами метра (например, к(>, х>), важно, чтобы существовала возможность статической проверки типов, гарантирующая, что этот вызов включает правильное количество фактических параметров, а их типы соответствуют типам формальных параметров вызываемой подпрограммы. Поскольку фактическое имя вызываемой через формальный параметр подпрограммы не известно в точке вызова (в нашем примере это > л при нервом вызове и >'л2 при втором), то обь>чно компилятор не может без дополнительной информации определить, соответствуют ли фактические параметры > н х в обращении Р(>, х1 тем, которые ожидаются подпрограммой >л или >л2.
Компилятору трсбуется полная спецификация формального параметра к, где указан не только тип процедура или фульвия, но также количество, порядок и тип каждого параметра (и рсзультата) этой процедуры или функции (как, например, в спецификации подпрограммы 0). Тогда в пределах подпрограммы 0 каждый вызов Р может быть статически проверен на правильность его списка параметров. Помимо этого для каждого вызова 0 можно проверить, совпадает ли спецификация фактичсского параметра, соотвстству>ошего Р, спецификации, заданной для формального параметра К.
Таким образом, и подпрограмма Гп, и подпрограмма Гл2 должны иметь одинаковое количество, порядок и типы параметров в соответствии со спецификацией для формального параметра й, Пелохольные ссылки (свободные переменные). Предположим, что подпрограмма, ~акая как >л или >"'п2, содср>кит ссылку на нелокальную переменную. Например, предположим, что в >л присутствует ссылка на г, а в тл2 — на г2, и ни в одной из подпрограмм нет локального определения для переменной, на которую они ссылаются. Такая нслокальная ссылка часто называется свободной переменной, так как она не имеет локального связывания в пределах определения подпрограммы.
Обычно, когда вызывается подпрограмма, формируется среда оелокачьных ссылок, и эта среда используется во время выполнения подпрограммы в качестве средства для определения значения каждой ссылки на нелокальную переменную (как описано в следу>ощих разделах). По предположим, что подпрограмма >л, в которой содержится нелокалы>ая ссылка, передается в качестве параметра из вызываюшей подпрограммы Р в вызванную подпрограмму 9. Какая среда нслокальных ссылокдолжна использоваться, когда >л вь>зывается в 0 (при использовании соответствующего формального параметра й, например Р(>, х) для вызова>о(>, х1)? На первый взгляд, самый простой ответ заключается в том, что среда нслокальных ссылок должна быть такой же, как сели бы в подпрограмме 0 вызов РО.х> был просто заменен вызовом (и(>, хц но оказывается, что в большинстве случаев этот ответ будет неверным.
В листингс 9.9 приведена программа, иллюстрирующая возникаюнгую трудность. Функция (л содержит нслокальные ссылки на х и на >. Согласно правилам определения статической области видимости языка Рааса!, идентификатор х ссылается на переменную х, объявленную в главной программе, а > — на переменную >, объявленную в процедуре Р. Тем ое менее Р псредает >и в качестве параметра процедуре 9, и имснно ага процедура в действительности вызывает гл через имя формального параметра Р. В 0 имеются локальные определения как для >, так и для х, и нельзя позволить гл использовать эти неправильные локальныс переменные при ее вызове. Проблема свободных переменных в функциях, передаваемых подпрограммам в качестве параметров, возникает нс только в языках с блочной структурой и пра- 9.3.
Передача параметров 433 вилами определения статической области видимости, подобных языку Разса1. Она также встречается в языке !.15Р и других языках, которые используют для определения среды нелокальных ссылок правило последней ассоциации, Общее ретпение проблемы заключается в использовании следую(цего правила определения значения свободных переменных в параметрах-функциях: нелокальная ссылка (ссылка на свободную переменную) во время выполнения подпрограммы, переданной в качестве параметра, должна означать то же самое, что она означала бы, если бы подпрограмма была вызвана в точке, где она появляется в качестве фактического параметра в списке параметров.
Например, в листинге 9.9 подпрограмма Ри появляется как фактический парам(тр в списке параметров при вызове О из Р. Таким образом, нелоквльные ссылки на х и т в ~и независимо от того, где ти будет фактически вызвана впоследствии (в нашем слччае внутри О), должны означать то же, что они означали бы, если бы тп была вызвана в том же месте, где вызвана О внутри Р. Листинг 9.9. Свободные переменные как параметры подпрограммы в языке Рааса! ргодгвю Мати; чвг х; зи(едег. ргосееоге О(чвг зм итедег.