【问题标题】:How do I declare an array when I don't know the length until run time?当我直到运行时才知道长度时如何声明数组?
【发布时间】:2010-09-30 19:10:35
【问题描述】:

我最初有一个数组[1..1000],它被定义为一个全局变量。 但是现在我需要将其设为 n,而不是 1000,并且直到稍后我才知道 n。 在填充数组之前我知道 n 是什么,但我需要它是全局的,因此需要一种方法来在运行时定义全局数组的大小。

上下文正在用文件中字节的线性变换填充数组。 我不知道文件有多大,直到有人想要打开它并且文件可以是任意大小。

【问题讨论】:

  • 这到底是什么语言?
  • 用于定义数组的 [lobound..hibound] 构造,加上全局数组和文件 I/O 只让 Pascal 和 Ada 在竞争中,所以赌注应该在 Delphi 上 :) Rob 重新标记不过,在你发表评论之后。

标签: delphi arrays global-variables dynamic-arrays redefine


【解决方案1】:

从 Delphi 4 开始,Delphi 支持动态数组。您可以在运行时修改它们的大小,它们将以旧大小保留您存储在其他元素中的数据。它们可以保存任何同类类型的元素,包括记录和其他数组。您可以像声明普通的“静态”数组一样声明动态数组,但只需省略数组边界:

var
  ArthurArray: array of TForm;

虽然静态数组允许您指定下限和上限,但动态数组的下限索引始终为零。高位索引由High 函数给出,它总是返回比数组长度小一的值。对于任何动态数组xHigh(x) = Length(x)-1

任何代码都可以访问全局变量,包括本地过程。

动态数组类型的全局变量将被初始化为一个数组。它的长度为零,在该数组上调用的High 将为-1。该数组上的Low 仍将返回零。

您可以随时调整动态数组的大小。使用SetLength 函数,就像使用字符串一样:

var
  NumElements: Integer;
begin
  NumElements := GetNumberOfArthurForms();
  SetLength(ArthurArray, NumElements);
end;

如果你有一个多维数组,你可以在一个循环中设置它们的长度:

var
  matrix: array of array of Double;
  i: Integer;
begin
  SetLength(matrix, height);
  for i := 0 to height - 1 do
    SetLength(matrix[i], width);
end;

有一个快捷方式可以一次设置所有内部数组的长度:

begin
  SetLength(matrix, height, width);
end;

就像我提到的,当您调整动态数组的大小时,动态数组会保留它们的旧值:

var
  data: array of string;
begin
  SetLength(data, 2);
  data[1] := 'foo';
  SetLength(data, 20);
  Assert(data[1] = 'foo');
end;

但如果你缩短数组,任何位于新最后一个元素之外的元素都将永远消失:

begin
  SetLength(data, 20);
  data[15] := 'foo';
  SetLength(data, 2);
  // data[15] does not exist anymore.
  SetLength(data, 16);
  writeln(data[15); // Should print an *empty* line.
end;

我上面的演示使用了字符串。字符串在 Delphi 中是特殊的;它们由编译器通过引用计数进行管理。因此,字符串类型的新动态数组元素被初始化为空。但如果我改用整数,则无法保证新元素的值。它们可能为零,但也可能是其他任何值,就像独立局部变量的初始值一样。

有人告诉我,Delphi 7 帮助文件非常好。请在此处阅读有关动态数组的更多信息。您可以在 Delphi 安装中提供的 VCL 和 RTL 源代码以及过去 10 年产生的几乎所有 Delphi 代码示例中找到它们的使用演示。

【讨论】:

  • +1,非常完整的答案。感人的。现在请有人编辑问题摘要以包含“Delphi”和“动态数组”,以便人们在搜索中找到它。
  • 其实,当你展开一个动态数组时,数组中新条目的内存是保证清零的,也就是说整数为0,指针为NIL,字符串(长字符串和短字符串都有) ) 为空。我无法在帮助中找到它的文档,但是在跟踪 SetLength 的代码时,它以调用 FillChar 结束,该调用清除了额外分配的内存。
  • 这是一个相对较新的发展,@Heartware。正如你所说,它没有记录以这种方式工作,所以,就像我说的,没有保证,事实上,the documentation even says that。当前的实现采用简单的方法并且总是初始化新元素。我很确定以前的汇编器实现使用数组的 RTTI 来确定需要初始化的内容。下一个实现可能会回到这种方式。也许 FillChar 新的更快实现消除了汇编器版本。
  • 我需要修改我之前对@Heartware 的评论。在我发布它时,链接文档说“新分配空间的内容未定义”。 现在,它说“新分配的空间设置为 0 或 nil”。它在 Delphi 2010 和 Delphi XE2 之间发生了变化。据我所知,实际的行为永远都是一样的。
  • 上一个例子最后一行缺少“]”:writeln(data[15);
【解决方案2】:

首先,这里是您问题第一部分的一般回答:

如果您的数组不再是静态的,您可能需要考虑使用 TList、TStringList 或 Contnrs 单元中的众多容器类之一。

它们可能更好地代表您正在做的事情,提供您可能需要的其他功能,例如排序或名称/值对,它们会根据您的需要动态增长,并且已经过很好的优化。


然后你说:

“上下文正在用文件中字节的线性变换填充一个数组。我不知道文件有多大,直到有人想要打开它并且文件可以是任意大小。”

对于您的具体问题,我会使用以下方法将字节加载到文件中:

  MyFileStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  Size := MyFileStream.Size - MyFileStream.Position;
  SetLength(Buffer, Size);
  MyFileStream.Read(Buffer[0], Size);

然后您可以轻松地使用 PChar 指针逐个遍历 Buffer 中的每个字符甚至每个字节,并按照您需要的方式对其进行转换。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 2011-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多