Шрифт:
Исключая одну маленькую деталь: если все параметры передаются по значению, у вызванной процедуры нет никакого способа возвратить результат в вызвавшую! Переданный параметр не изменяется в вызвавшей подпрограмме а только в вызванной. Ясно, что так работы не сделать.
Существуют два эквивалентных ответа на эту проблему. В Паскале Вирт предусмотрел параметры-переменные, которые передаются по ссылке. VAR параметр не что иное как наш старый друг параметр Фортрана с новым именем и расцветкой для маскировки. Вирт аккуратно обходит проблему «изменения литерала» так же как проблему «адрес выражения» с помощью простого средства, разрешая использовать в качестве фактических параметров только переменные. Другими словами, это тоже самое ограничение, которое накладывали самые ранние версии Фортрана.
Си делает ту же самую вещъ, но явно. В C все параметры передаются по значению. Однако одним из видов переменных, которые поддерживает С, является указатель. Так передавая указатель по значению, вы в действительности передаете то, на что он указывает по ссылке. В некоторых случаях это работает даже еще лучше, потому что даже хотя вы и можете изменить указываемую переменную на все, что хотите, вы все же не сможете изменить сам указатель. В функции типа strcpy, к примеру, где указатель увеличивается при копировании строки, мы в действительности увеличиваем только копии указателей, так что значение указателей в вызвавшей процедуре все еще остается каким было. Чтобы изменить указатель вы должны передавать указатель на указатель.
Так как мы просто проводим эксперименты, мы рассмотрим и передачу по значению и передачу по ссылке. Таким образом у нас будет возможность использовать любой из них как нам нужно. Стоит упомянуть, что было бы тяжело использовать здесь подход С, так как указатель это другой тип а типы мы еще не изучали!
Передача по значению
Давайте просто попробуем некоторые нехитрые вещи и посмотрим, куда они нас приведут. Давайте начнем со случая передачи по значению. Рассмотрим вызов процедуры:
FOO(X, Y)
Почти единственным приемлемым способом передачи данных является передача через стек ЦПУ. Поэтому, код который мы бы хотели видеть сгенерированным мог бы выглядеть так:
MOVE X(PC),-(SP) ; Push X
MOVE Y(PC),-(SP) ; Push Y
BSR FOO ; Call FOO
Это конечно не выглядит слишком сложным!
Когда BSR выполнен центральный процессор помещает адрес возврата в стек и переходит к FOO. В этой точке стек будет выглядеть следующим образом:
.
.
Значение X (2 bytes)
Значение Y (2 bytes)
SP –> Адрес возврата (4 bytes)
Так что значения параметров имеют адреса с фиксированными смещениями от указателя стека. В этом примере адреса такие:
X: 6(SP)
Y: 4(SP)
Теперь рассмотрим, на что могла бы походить вызываемая процедура:
PROCEDURE FOO(A, B)
BEGIN
A = B
END
(Помните, что имена формальных параметров произвольные... учитываются только позиции).
Желаемый код мог бы выглядеть так:
FOO: MOVE 4(SP),D0
MOVE D0,6(SP)
RTS
Обратите внимание, что для адресации формальных параметров нам будет необходимо знать, какую позицию они занимают в списке параметров. Это подразумевает некоторые изменения в содержимом таблицы идентификаторов. Фактически, в нашем односимвольном случае лучше всего просто создать новую таблицу идентификаторов для формальных параметров.
Давайте начнем с объявления новой таблицы:
var Params: Array['A'..'Z'] of integer;
Нам также необходимо отслеживать, сколько параметров имеет данная процедура:
var NumParams: integer;
И мы должны инициализировать новую таблицу. Теперь, не забудьте, что список формальных параметров будет различным для каждой процедуры, которые мы обрабатываем, так что мы будем должны инициализировать эту таблицу заново для каждой процедуры. Вот инициализатор:
{–}
{ Initialize Parameter Table to Null }
procedure ClearParams;
var i: char;
begin
for i := 'A' to 'Z' do
Params[i] := 0;
NumParams := 0;
end;
{–}
Мы поместим обращение к этой процедуре в Init и также в конец DoProc:
{–}
{ Initialize }
procedure Init;
var i: char;
begin
GetChar;
SkipWhite;
for i := 'A' to 'Z' do
ST[i] := ' ';
ClearParams;
end;
{–}
.
.
.
{–}
{ Parse and Translate a Procedure Declaration }
procedure DoProc;
var N: char;
begin
Match('p');
N := GetName;
FormalList;
Fin;
if InTable(N) then Duplicate(N);
ST[N] := 'p';
PostLabel(N);
BeginBlock;
Return;
ClearParams;
end;
{–}