Извикване по референция и извикване по стойност на резултата

Е, имаше дебат относно кода по-долу между мен и моя приятел. Ние сме малко объркани относно резултата, който произвежда. Може ли някой да изясни резултата от стойността на извикване по референция и повикване за следната част от кода?

program params;
    var i: integer;
    a: array[1..2] of integer;

    procedure p(x,y: integer);
    begin
        x := x + 1;
        i := i + 1;
        y := y + 1;
    end;

begin
    a[1] := 1;
    a[2] := 2;
    i := 1;
    p( a[i],a[i] );
    output( a[1],a[2] );
end.

Резултатът от тази програма в случай, че параметрите се предават на процедура p чрез стойност-резултат и чрез препратка.


person Srujan Simha    schedule 26.09.2015    source източник


Отговори (2)


Извикване по стойност

x и y в p са локални променливи, инициализирани с действителните параметри, докато i е глобална променлива, така че извикването p( a[i],a[i] ) е еквивалентно на:

x := 1  /* The value of a[i] */
y := 1  /* The value of a[i] */

x := 2  /* x + 1 */
i := 2  /* i + 1 */
y := 2  /* y + 1 */

и накрая се отпечатват стойностите 1, 2, тъй като те са стойностите на a[1], a[2], които не са променени.

Обаждане чрез справка

И x, и y в p са псевдоними за a[1] и (отново) a[1] (от i = 1, когато се извиква процедурата), така че извикването е еквивалентно на:

a[1] := 2  /* a[1] + 1 */
i    := 2  /* i + 1 */
a[1] := 3  /* a[1] + 1 */

и накрая се отпечатват стойностите 3, 2.

Обаждане по име

Извикването по име е еквивалентно на извикването по препратка, когато прости променливи се предават като параметри, но е различно, когато предавате израз, който обозначава място в паметта, като долен индекс. В този случай действителният параметър се преоценява всеки път, когато се срещне. Така че в този случай това е ефектът от извикването на p( a[i],a[i] ):

a[1] := 2  /* since i = 1, the result is equal to a[1] + 1 */
i    := 2  /* i + 1 */
a[2] := 3  /* since i is now 2, the result is equal to a[2] + 1 */

и накрая се отпечатват стойностите 2, 3. На практика реализацията извиква анонимна функция („thunk“), всеки път, когато трябва да оцени параметър.

Резултат от извикване по стойност

Само за да завършим дискусията, тук е случаят с предаването на параметър стойност-резултат, при който x и y се инициализират в началото на изпълнението на процедурата със стойностите на действителните параметри и в края на изпълнението на процедура, се копират обратно към оригиналните променливи адреси:

x := 1       /* The value of a[i] */
y := 1       /* The value of a[i] */

x := 2       /* x + 1 */
i := 2       /* i + 1 */
y := 2       /* y + 1 */

a[1] := 2    /* the value of x is copied back to a[1] */
a[1] := 2    /* the value of y is copied back to a[1] (not a[2]!) */

и накрая се отпечатват стойностите 2, 2.

За обсъждане на различните начини за предаване на параметри вижте например това.

person Renzo    schedule 26.09.2015
comment
Благодаря за обяснението. Бихте ли предоставили резултата за call by value result? - person Srujan Simha; 26.09.2015
comment
Добавих извикването по стойност-резултат. - person Renzo; 26.09.2015
comment
Не знаех, че Pascal поддържа извикване по име и извикване по стойност на резултата. Как е формулиран този режим? - person jwdietrich; 27.09.2015
comment
Тъй като Pascal е само в етикета, а не изрично във въпроса, в моя отговор говорех като цяло за предаване на параметри за езици, които са повлияли или са били повлияни от (различните версии на) Pascal. Например поне един текущ компилатор на Pascal (Free Pascal) има извикване по стойност, извикване по референция (var), извикване по резултат (out) (и също извикване по константа (const)); Algol 60 има извикване по име; Ada и други езици имат извикване по стойност-резултат (in out). - person Renzo; 27.09.2015
comment
защо се копира обратно в [1]? когато говорим за x, в тялото на функцията имаме x = 2 . Така че защо 1 се копира обратно в a[1] от x? - person brucebanner; 21.06.2021
comment
@brucebanner, прав си, текущата стойност на x, 2, се копира обратно в [1]. Коригирах отговора. Благодаря! - person Renzo; 21.06.2021

procedure p(x, y: integer);
begin
end;

В този случай променливите, предавани като параметри, никога не се променят. Те се копират или в два регистъра (вероятно x: EAX и y: ECX) или в стека. (в зависимост от ABI на компилатора)

procedure p(var x, y: integer);
begin
end;

В този случай оригиналните параметри се променят. x и y са указатели към оригиналните променливи, предадени като параметри.

person Abstract type    schedule 26.09.2015