Most
parameters are either value parameters (the default) or variable (var)
parameters. Value parameters are passed by value, while variable
parameters are passed by reference. To see what this means,
consider the following functions.
function
DoubleByValue(X: Integer): Integer; // X is a value parameter
begin
X := X * 2;
Result := X;
end;
function
DoubleByRef(var X: Integer): Integer; // X is a variable parameter
begin
X := X * 2;
Result := X;
end;
These
functions return the same result, but only the second one – DoubleByRef
can change the value of a variable passed to it. Suppose we call the functions
like this:
var
I, J, V, W: Integer;
begin
I := 4;
V := 4;
J := DoubleByValue(I); // J = 8, I = 4
W := DoubleByRef(V); // W = 8, V = 8
end;
After
this code executes, the variable I, which was passed to DoubleByValue,
has the same value we initially assigned to it. But the variable V, which was
passed to DoubleByRef, has a different value.
A value
parameter acts like a local variable that gets initialized to the value passed
in the procedure or function call. If you pass a variable as a value parameter,
the procedure or function creates a copy of it; changes made to the copy have
no effect on the original variable and are lost when program execution returns
to the caller.
A
variable parameter, on the other hand, acts like a pointer rather than a copy.
Changes made to the parameter within the body of a function or procedure
persist after program execution returns to the caller and the parameter name
itself has gone out of scope.
Even if
the same variable is passed in two or more var parameters, no copies are
made. This is illustrated in the following example.
procedure
AddOne(var X, Y: Integer);
begin
X := X + 1;
Y := Y + 1;
end;
var
I: Integer;
begin
I := 1;
AddOne(I, I);
end;
After
this code executes, the value of I is 3.
If a
routine’s declaration specifies a var parameter, you must pass an
assignable expression, that is, a variable, typed constant (in the {$J+}
state), dereferenced pointer, field, or indexed variable to the routine when
you call it. To use our previous examples, DoubleByRef(7) produces an error,
although DoubleByValue(7) is legal.
Indexes and pointer dereferences passed in var parameters. For example, DoubleByRef(MyArray[I]) are evaluated once, before execution of the routine.
大多数参数是值参数(缺省)或变量(var)参数。值参数传递值(by value),变量参数传递引用(by reference)。要明白这一点,可以考虑下面的函数,
function
DoubleByValue(X: Integer): Integer; // X是一个值参数
begin
X := X * 2;
Result := X;
end;
function
DoubleByRef(var X: Integer): Integer; // X是一个变量参数
begin
X := X * 2;
Result := X;
end;
上面的函数返回相同的结果,但只有第二个函数 – DoubleByRef可以改变传递给其的变量的值。假设如下调用这两个函数:
var
I, J, V, W: Integer;
begin
I := 4;
V := 4;
J := DoubleByValue(I); //执行后
J = 8, I = 4
W := DoubleByRef(V); //执行后 W = 8, V = 8
end;
这些代码执行后,传递给DoubleByValue的变量 I 与最初赋给它的值相同。而传递给DoubleByRef的变量 V 则具有不同的值。
值参数的表现就象一个具有初始化值并传递到过程或函数调用中的局部变量。如果把一个变量作为值参数传递,那么过程或函数会创建一个副本;对值参数的改变只改变副本而不影响原始变量,并且当程序执行返回给调用者时这些副本被自动销毁。
另一方面,变量参数的表现就象一个指针副本。在函数或过程主体中对变量参数的改变将一致持续,直到程序执行返回调用者并且变量参数名自身已经离开其作用域。
即使相同的变量作为两个或更多的变量(var)参数被传递,也不会创建任何副本。例如,
procedure
AddOne(var X, Y: Integer);
begin
X := X + 1;
Y := Y + 1;
end;
var
I: Integer;
begin
I := 1;
AddOne(I, I);
end;
这些代码执行后,变量 I 的值为3。
如果一个例程的声明指定了一个变量(var)参数,那么当调用该例程时必需向其传递可赋值的表达式,如变量、类型常量(在
{$J+} 状态下)、解除参照的指针、域,或者被索引的变量。对于前一个例子(DoubleByValue和DoubleByRef),DoubleByRef(7)将导致错误,而DoubleByValue(7)是合法的。
索引和指针解除参照可以作为变量(var)参数传递。例如,DoubleByRef(MyArray[I])在例程执行之前将被求值一次。
编者注
值参数相当于例程内部的局部变量,其内存分配自动完成:调用例程时分配,返回调用者时释放。因此在例程内部对其改变仅仅作用于参数值的副本。如果传递的是一个常量或者需要求值的表达式,那么常量或表达式的值回被存储到新创建的局部变量中。
变量参数相当于变量指针的别名,在例程内部并不为其分配内存。对于例程中每一次引用,都会自动解释成为传递进来的原始实体(变量或其他可赋值的表达式)。
由此可见,如果要将长串作为参数传递,则应当尽可能定义为变量(var)参数,以减少内存开销和提高代码效率。当然,这只是针对值参数和变量(var)参数而言。有时还需要考虑将长串参数声明为输出(out)参数,见Out参数。