Вызов по ссылке и результат по стоимости
Ну, между мной и моим другом возникла дискуссия по поводу приведенного ниже кода. Мы немного озадачены тем, что он производит. Может ли кто-то уточнить результат вызова по ссылке и по значению вызова для приведенного ниже фрагмента кода?
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 по значению-результату и по ссылке.
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"), каждый раз, когда она должна оценить параметр.
Call по значению Результат
Просто для завершения обсуждения, вот случай передачи параметра значение-результат, в котором 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] := 1 /* 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 печатаются.
Для обсуждения различных способов передачи параметров смотрите, например, это.
procedure p(x, y: integer);
begin
end;
В этом случае переменные, передаваемые как параметры, никогда не изменяются. Они копируются либо в два регистра (вероятно, x: EAX и y: ECX), либо в стек. (в зависимости от компилятора ABI)
procedure p(var x, y: integer);
begin
end;
В этом случае исходные параметры будут изменены. x
а также y
являются указателями на исходные переменные, передаваемые в качестве параметров.