Вызов по ссылке и вызов по результату значения

Что ж, между мной и моим другом был спор о приведенном ниже коде. Мы немного смущены выводом, который он производит. Может ли кто-нибудь прояснить результат вызова по ссылке и вызова по значению для приведенного ниже фрагмента кода?

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. На практике реализация вызывает анонимную функцию («преобразователь») каждый раз, когда она должна оценивать параметр.

Вызов по значению результата

Просто чтобы завершить обсуждение, вот случай передачи параметра значение-результат, в котором 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
Я не знал, что Паскаль поддерживает вызов по имени и результат вызова по значению. Как сформулирован этот режим? - person jwdietrich; 27.09.2015
comment
Поскольку Паскаль указан только в теге, а не в вопросе явно, в своем ответе я говорил в целом о передаче параметров для языков, которые повлияли или находились под влиянием (различных версий) Паскаля. Например, по крайней мере текущий компилятор Pascal (Free Pascal) имеет вызов по значению, вызов по ссылке (var), вызов по результату (out) (а также вызов по константе (const)); Алгол 60 имеет вызов по имени; Ада и другие языки имеют вызов по значению-результату (in out). - person Renzo; 27.09.2015
comment
почему он 1 копируется обратно в [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