在Pascal中,如果我通过将一个非初始化的数组赋值给它来清空我的数组,会发生什么不好的事情吗?

3
在Pascal中,我唯一敢尝试清空数组的方式是通过迭代遍历并清空它,但这种方法非常低效。我不能只是通过将一个空数组分配给它来重新初始化它吗?
program arrays;
  var
    working, empty : array [1..10] of integer;
begin
  working[3] := 5;
  working:= empty;
end.

这样做可以吗?会不会产生反效果?

3个回答

8

如果您想要清空数组,可以这样写:

working:= empty;

事实上,通过将“空”数组内容复制到“工作中”,实际上进行了清除...在您的情况下,“空”是void,因为它是全局变量,因此初始化为0。
在我看来,定义这样的全局变量并不是一种好的做法。在大多数情况下,全局变量都是有害的(除非您知道自己在做什么),并且声明它们被初始化为0在某些情况下是没有意义的。
事实上,如果“空”在堆栈上初始化(即方法内的var),则会填充堆栈上此时存在的任何内容,即一些随机数据。
如果您想快速初始化不包含任何引用计数类型(如字符串)的数组,则可以编写:
fillchar(working,sizeof(working),0);

如果你的数组包含托管类型,你可以这样写:

finalize(workingWithStringInside); // to safely release internal managed types
fillchar(workingWithStringInside,sizeof(workingWithStringInside),0);

这是最快的代码(比变量复制更快),听起来是一个更好的选择。

为什么它更快?我本以为对于一个小数组,赋值操作很难被超越。但对于较大的数组,你会耗尽处理器缓存。当然,我不认为性能在这里是至关重要的,过早地进行优化是万恶之源。个人而言,我永远不会使用FillChar方法。我会使用一个通用函数,在循环中执行填充和赋值操作。 - David Heffernan
1
@DavidHeffernan 赋值 = 读取 + 写入;填充字符 = 写入;循环 = 计数 * 写入。在实践中,循环可以确实足够快,但比填充字符慢。对于简单的整数数组,这是可以接受的。但对于记录数组可能难以维护,因为所有成员都必须显式命名并赋予空值。这可能会令人困惑。如果您使用固定大小的数组,填充字符在我看来是一个不错的选择。如果您想要一些干净、快速和高级的东西,动态数组或专用的泛型类型是最好的方式。 - Arnaud Bouchez
一个空记录 const nullrec: TMyRec=() 可以被使用,或者在现代 Delphi 中使用 Default(TMyRec)。 - David Heffernan

5

这是绝对没问题的。代码语义正是您所需要的。肯定Delphi编译器将生成执行简单高效的内存复制的代码。编译器可以这样做是因为您有一个固定长度的数组,其元素是简单值类型。如果FPC没有产生非常相似的代码,我会感到惊讶。

即使您的数组包含托管类型(它没有),赋值运算符也会产生尊重这些托管类型的代码。

最后一条评论是,装满零的数组应该是一个常量。


2
好的,那我猜我将以这种方式清除数组,就内存而言,但我认为它不够高效,因为它使用了两倍的内存,对吗? - Vincas Stonys
3
没错。如果你想避免使用额外的数组,那么你可以使用FillChar。只要你的数组持有值类型,这样做就没问题。 - David Heffernan
在这个 Unicode 的时代,您还会使用 FillChar 吗?还是这个名称来自往日的错误信仰? - Marjan Venema
2
@MarjanVenema 我个人不会这么做。我会像这样写:TArray.Initialise<Integer>(working, 0);使用我自己的 TArray 类,它扩展了 Generics.Collections.TArray 并具有一堆有用的静态类方法。 - David Heffernan
1
@MarjanVenema 另外,Unicode 和 FillChar 不会互相影响。只是 FillChar 的命名不正确。它应该被命名为 FillByte - David Heffernan
显示剩余3条评论

0
一个简单的方法是不在类型中设置变量的长度...并使用SetLength为您初始化数组...来自Delphi帮助:当S是必须初始化的类型的动态数组时,新分配的空间将设置为0或nil。
type
  TIntArray =  Array of Integer;

procedure WorkArrays(var aWorking: array of integer);
begin
  if High(aWorking) >= 0 then
    aWorking[0] := 1;
  if High(aWorking) >= 3 then
    aWorking[3] := 5;
end;

procedure WorkArrays2(var aWorking: array of integer);
begin
  if High(aWorking) >= 1 then
    aWorking[1] := 4;
  if High(aWorking) >= 9 then
    aWorking[9] := 7;
end;

procedure WorkArrays3(var aWorking: TIntArray);
begin
  SetLength(aWorking, 0);
  SetLength(aWorking, 4);
  aWorking[0] := 1;
  aWorking[3] := 5;
end;

procedure WorkArrays4(var aWorking: TIntArray);
begin
  SetLength(aWorking, 0);
  SetLength(aWorking, 10);
  aWorking[1] := 4;
  aWorking[9] := 7;
end;

procedure TForm58.ShowArrays(aWorking: array of integer);
var
  a_Index: integer;
begin
  for a_Index := Low(aWorking) to High(aWorking) do
    Memo1.Lines.Add(IntToStr(aWorking[a_Index]));
end;

procedure TForm58.ShowArrays2(aWorking: TIntArray);
var
  a_Index: integer;
begin
  for a_Index := Low(aWorking) to High(aWorking) do
    Memo1.Lines.Add(IntToStr(aWorking[a_Index]));
end;

procedure TForm58.Button1Click(Sender: TObject);
var
  a_MyArray: array of integer;
  a_MyArray1: TIntArray;
begin
  //SetLength(aWorking, 0);
  SetLength(a_MyArray, 3);//note this is a Zero based Array...0 to 2
  WorkArrays(a_MyArray);//note aWorking[3] will not show...because High is 2...
  ShowArrays(a_MyArray);
  SetLength(aWorking, 0);
  SetLength(a_MyArray, 10);//note this is a Zero based Array...0 to 9
  WorkArrays2(a_MyArray);
  ShowArrays(a_MyArray);
  WorkArrays3(a_MyArray1);
  ShowArrays2(a_MyArray1);
  WorkArrays4(a_MyArray1);
  ShowArrays2(a_MyArray1);
end;

我在提出这个问题之前,就尝试过使用SetLength初始化数组,但我非常确定它行不通。 - Vincas Stonys
你把它设为零了吗?这将确保你使用的是一个干净的数组。 - House of Dexter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接