按引用调用和按值调用结果
Call by Reference and Call by Value Result
好吧,我和我的朋友就下面的代码进行了辩论。我们对它产生的输出有点困惑。有人可以澄清下面这段代码的按引用调用和按值调用结果吗?
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。
procedure p(x, y: integer);
begin
end;
在这种情况下,作为参数传递的变量永远不会被修改。它们被复制到两个寄存器(可能是 x: EAX 和 y: ECX)或复制到堆栈。 (取决于编译器 ABI)
procedure p(var x, y: integer);
begin
end;
本例修改了原有参数。 x
和 y
是指向作为参数传递的原始变量的指针。
按值调用
x
和p
中的y
是局部变量用实际参数初始化,而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]
的值。
参考调用
p
中的 x
和 y
都是 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
在过程执行开始时使用实际参数,并在过程执行结束时,被复制回 原始 变量 addresses:
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。
有关传递参数的不同方式的讨论,请参阅 this。
好吧,我和我的朋友就下面的代码进行了辩论。我们对它产生的输出有点困惑。有人可以澄清下面这段代码的按引用调用和按值调用结果吗?
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。
procedure p(x, y: integer);
begin
end;
在这种情况下,作为参数传递的变量永远不会被修改。它们被复制到两个寄存器(可能是 x: EAX 和 y: ECX)或复制到堆栈。 (取决于编译器 ABI)
procedure p(var x, y: integer);
begin
end;
本例修改了原有参数。 x
和 y
是指向作为参数传递的原始变量的指针。
按值调用
x
和p
中的y
是局部变量用实际参数初始化,而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]
的值。
参考调用
p
中的 x
和 y
都是 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
在过程执行开始时使用实际参数,并在过程执行结束时,被复制回 原始 变量 addresses:
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。
有关传递参数的不同方式的讨论,请参阅 this。