Т. Пратт, М. Зелковиц - Языки программирования - разработка и реализация (4-е издание_ 2002) (1160801), страница 118
Текст из файла (страница 118)
426 Глава 9. Управление подпрограммами Когда Р вызывает О, вычисляются выражения фактических параметров а и 2Ь (то есть вызывается операция обработки ссылок для определения текущей ассоциации для имен з и Ь). Каждое имя представляет объект данных для целых переменных, так что переданные фактические параметры — это г-значение переменной з и йзпаченне переменной Ь. Поскольку з передается по значению, формальный параметр 1 представлен локальной целой переменной внутри подпрограммы О. Когда подпрограмма 0 начинает выполняться, значение переменной з в момент вызова 0 присваивается формальному параметру 1 как его начальное значение.
Далее между а и ! какая-либо связь отсутствуе~. Позтому, когда ! присваивается новое значение 12, то а не меняется. После завершения вызова 0 значение переменной а по-прежнему равно 2, В отличие от з фактический параметр Ь передастся по ссылке. Это означает, что формальный параметр 3 представляется локальной переменной подпрограммы О, типом которой является указатель ни целочисленный объект далнь х. Когда 0 начинает выполняться, ука птгель па объект данных Ь сохраняется как гзначение формального параметра 3.
Когда к значению формального параметра 3 добавляется 10, 3 не меняется. Вместо зтого кажлая ссылка на 3 (г-значение 3, которос является йзначением Ь) после выполнения операции выборки в соответствии с заданным значением указателя в действительности получает доступ к местоположению объекта данных Ь.
В итоге операция присваивания переменной 3, хотя и выглядит так жс, как присваивание нового значения переменной ц имеет совершенно иной смысл, Значение фактического параметра Ь изменилось, теперь оно равно 13. При выводе ~а печать значений параметров ! и 3 в подпрограмме 0 получается соответственно 12 и 13. После возвращения в подпрограмму Р значения соответствующих фактических параметров з и Ь снова выводятся на печать. Видно, что изменилось только значение переменной Ь. Значение 12, присвоенное ! в подпрограмме О, теряется по ее завершении, поскольку локальные переменные уничтожаются после окончания работы подпрограммы.
Значение 3, конечно, тоже утрачивается, но это значение является указателем на объект данных, а не числом 13, Листинг 9.7. Передача параметров по ссылке и по значению в языке ОЬ а — вызванная подпрограмма, б — вызывающая подпрограмма а! ОП пт и !пт "!) ) !ы~!О; *з=*1+!О. Рг~пгпы =Хтк Ь-тз.щ" ! *!!; ) ш Р1! )~пт а. ь, а=2; Ь =- 3 0)ь,ЗЬК Рг!ЬЬИ "ЖЬ Гщп".а.ЬП ) Структуры данных. Предположим, что мы хотим написать другую версию подпрограммы О, в которой формальными параметрами являются векторы.
На С та- 9.3. Передача параметров 42Т кую подпрограмму написать сложно, поскольку обычно в С значения массивов не передаются по значению. Объявление процедуры ец01() и[ а[20] ) в С интерпретируется так же, как если бы бьио записано зцЫ(1п[ *а), то есть в С передача массива осуществляется так же, как в Айа 9э, передачей не его и-значения (копии массива), а указателя на массив (его (-значения). По этой причине данный пример мы приводим ца языке Разса!, который скопирует и-значенпе массива-параметра, передаваемого по значению в подпрограмму: туре чест = аггау [ 1 .3] от 1и едет; Ргосеооге 0(Ы чесц чаг 1; чесы: чаг и: 1итедег; Ьед!и 2[21:= к[2] ч 10; 1[2] = 1[2] ч И; Гог и := 1 бо 3 ао нг1[о(К[и]), Гог и ;= 1 та 3 оо нг1[е(1[и]) еие: Процедуру Р можно записать так, как это сделано в листинге 9.8.
После ее выполнения будут напечатаны следующие значения; б 17 8 б 17 8 б 7 Яб1! Я. Листинг 9.8. Структуры данных как параметры подпрограммы ргосеооге Р. чаг с.щ чесал и: 1итедег; Ьед)и с[ П - б с[2] :- 7; с[3] :- Я; 6[1] .= б. 0[2] = 7; И[3] .= В. О( '.0), Гог и = 1 то 3 По нг1те(с[я]): Гог и - 1 то 3 сто нгцещ[я]) еигд Чтобы проследить передачу параметров с и г], сначала следует заметить, что вычисление выражений фактических параметров с и (] в Р приводит к указателям на блоки памяти для векторов с и (] в записи активации подпрограммы Р (как в пре- дыдущем примере для параметров а и Ь).
Эти указатели передаются подпрограмме О. Поскольку вектор с передается по значению, соответствующий формальный па- раметр к является локальным массивом в подпрограмме О, имеющим такой же вид, что и с (три компонента плюс дескриптор). Три компонента вектора с копируются в соответствующие компоненты вектора к, после чего с и х более не взаимодей- ствуют. Поэтому, когда управление возвращается к подпрограмме Р, вектор с оста- ется неизменным, даже сели в подпрограмме О происходили присваивания новых значений компонентам вектора х. Но вектор ф который передается по ссылке, из- меняется в результате присваивания новых значений компонентам вектора 1, яв- ляющегося форма))ьным параметром, так как 1 — это не вектор, а лишь указатель на вектор.
Когда начинает выполнят) ся подпрограмма О, формальный параметр 1 ишщиализируется указателем на вектор ф и каждая последующая ссылка на него в О приводит через этот указатель к О. Таким образом, приснан ванне нового значе- ния компоненту 1[2] также изменяет и компонент (][2]. Печатаемые значения от- ражают эти отличия.
На рис. 9.7 изображен стек времени вьпюлненпя перед завер- шением подпрограммы О. 428 Глава 9. Управление подпрограммами Запись активации для Р „„' Формальный параметр К'. , является копией ) Фактического , параметра с Запись активации для С) ОСР ЗСР К ; Формальный параметр! ', указывает на Фактический ', параметр Е и ССР— указатель динамической цепочки ЗСР— указатель статической цепочки Рио. 9.7.
Стек времени выполнения перед завершением подпрограммы С) Вообще говоря, такая структура данных, как массив или запись, передаваемая позначспию,обы щокоппруетсн всоответствующуюструктуруданпых(формальный параметр) в вызванной подпрограмме, которая работает с этой локальной копией и не имеет доступа к оригиналу.
Структура данных, переданная по ссылке, не копируется, а вызванная подпрограмма работает непосредственно со структурой данных, представляющей фактический параметр (используя для доступа указатель, хранящийся в формальном параметре). Компоненты структур данных. Вернемся к процедуре 0 из листинга 9.7 (а), по теперь вместо простых переменных илп констант передадим 0 в качестве параметров компоненты каких-либо структур данных — например, написав подпрограмму Р следующего вида: Р() (~п[ с[4].
)и[ и. с[)] = 6: с[2] = ), с[3] = а 0(с[Ц Ас[2]) . Гог (в = 1; ьсыз: (и+т) Рг)п[(("то)п",с[а]): Когда Р выполнится, будут напечатаны значения: 16 1/ б 17 8. 9.3. Передача параметров 429 Передача сП1 по значению происходит по тому же сценарию, что и прежде. Выражение сШ вычисляется получением ссылки на вектор с и последуюгцим выбором первого компонента.
Результатом будет г-значение этого компонента. Формальный параметр ~ инициализируется, как и прежде, этим вычисленным тзначением, поэтому и остальные действия остаются такими же, как и в случае передачи переменных. Подобным же образом вычисляется ее[21, и в подпрограмму 0 передается указатель на компонент вектора. Тогда операции присваивания в полпрограмме Я непосредственно изменяют компонент вектора с через указатель, хранящийся в формальном параметре 3.
Компоненты вектора с представлены в памяти точно также, как были бы представлены обьекты данных простых переменных того же типа. Выполняемый код подпрограммы О, которьпй манипулирует формальными параметрами ~ и 3, будет одним и тем жс как при вызове 0[а. Ь), так и при вызове 0[с[П, с[21). Если бы компоненты массива с были представлены в памяти каким-либо образом, отличным от представления простых переменных а и 5 (например, были бы каким-то образом унакованы), то до вызова подпрограммы 0 потребовалось бы преобразовать фактические параметры к нужному нилу (который ожидается в 0), разместить эти преобразованные параметры во временной памяти и передать указатели на расположение во временной памяти этих параметров в подпрограмму О.
Прн передаче по значеншо этих преобразований было бы вполне достаточно, но при передаче по ссылке передаваемый указатель больше не является указателем на исходный объект лап ных, поэтому присваивания в подпрограмме 0 больше не модифицируют непосредственно фактический параметр. По этой причине передача компонентов упакованных массивов и записей часто запрещается (как, например, в Раэса1). Компоненты массивов с вычисляемыми индексами. Предположим, что в подпрограмме и имеются два целочисленных параметра, передаваемых по ссылке: а[т[ *и тс *)) (*~ = *~ *) рг~осг[ "м хвзт,"ъ*)); Если в подпрограмме Р вместо вызова 0 подставить вызов К то получится: Р[) ("пс с[4): гт в; с[)) = 6; с[2) — 7, с[3) — 8, н — 2.