我正在尝试使用变量数组实现可变数量参数的过程。 问题是对传递的变量的更改在过程之外是不可见的。
procedure arrChange(var arr: array of String);
var
i: Integer;
begin
for i := 0 to Length(arr) do
arr[0] := 'changed';
end;
procedure strChange(var str: String);
begin
str := 'changed';
end;
procedure strChangeNotVariable(str: String);
begin
str := 'changed again';
end;
var a, b: String;
begin
a := 'a';
b := 'b';
print(a); // prints a - expected
print(b); // prints b - expected
arrChange ([a, b]);
print(a); // prints a - why?
print(b); // prints b - why?
strChange(a);
strChange(b);
print(a); // prints changed - expected
print(b); // prints changed - expected
strChangeNotVariable(a);
strChangeNotVariable(b);
print(a); // prints changed - expected
print(b); // prints changed - expected
end.
我能否解释为什么arrChange之外的变化不可见和/或可能的解决方法?
答案 0 :(得分:1)
我想您应该知道开放数组参数:有一种叫做开放数组构造函数的东西。如果你没有将一个真正的现有数组传递给这样的 open数组参数,而是用方括号括起来的一堆值,就会发生这种情况。
对于此类开放数组构造函数,编译器会编写在堆栈上创建ad hoc数组的代码(它会保留堆栈空间,然后将值放入其中)和然后将指向该ad hoc数组的指针及其大小减去1传递给该过程。只要程序运行,这样的ad hoc数组才有效,即只有程序。当程序退出时,它们会消失。这就是为什么它们不能作为var
(引用)参数传递的原因。有些版本可能允许它,但这只会改变堆栈上的副本,而不是原件。
如果你想要你的技巧,你必须传递一个真正的现有数组。这可能是这样的:
var
d: array of string;
...
SetLength(d, 2);
d[0] := a;
d[1] := b;
arrChange(d);
现在,如果您在此过程后打印d[0]
和d[1]
,则应显示'changed'
。但a
和b
仍应保持完整且不得更改,因为d
仅包含副本。
只有在您传递a
时才会直接影响b
和array of PString
。但是,仍然没有必要使开放数组成为参考参数。我试过这个:
procedure arrChange(const arr: array of PString);
var
i: Integer;
begin
for i := 0 to High(arr) do
// Not modifying arr[i] itself, only to what it points!
arr[i]^ := 'changed'; // FWIW, you had arr[0] instead of arr[i] ;-)
end;
和
arrChange([@a, @b]);
print(a);
print(b);
工作和制作
changed
changed
带
procedure print(const s: string);
begin
Writeln(s);
end;
更多信息:Open array parameters and array of const。
FWIW,似乎编译器甚至没有更新字符串的引用计数。它将指针的原始副本放在堆栈上,就像const
字符串一样。