对变量数组元素(变量参数)的更改在过程外部不可见

时间:2018-01-27 15:13:53

标签: delphi pass-by-reference

我正在尝试使用变量数组实现可变数量参数的过程。 问题是对传递的变量的更改在过程之外是不可见的。

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之外的变化不可见和/或可能的解决方法?

1 个答案:

答案 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'。但ab仍应保持完整且不得更改,因为d仅包含副本。

只有在您传递a时才会直接影响barray 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字符串一样。