当前位置:首页>java>Delphi 编程 -- 再谈数组

Delphi 编程 -- 再谈数组

  • 2026-10-29 11:12:31
Delphi 编程 -- 再谈数组
上一篇我们谈了 Delphi 中的静态数组,特别是 Delphi 针对原始 Pascal 的限制提出的开放数组参数解决方案。今天我们来谈动态数组。
动态数组结合了空间可以动态变化与元素可以通过索引访问这两方面的优点,可以认为动态数组的“结构是动态的,访问方式是静态的”。
Delphi 中的静态数组类型可以表示如下:
而动态数组类型可以表示如下:
数组声明举例如下:
var  sa: array[3..5] ofinteger;  //staticarray  da: arrayofinteger;        //dynamicarray
由以上的声明可见,sa 之所以是“静态”,是因为在数组变量声明之时,即指定了该数组的边界,亦即数组的尺寸。这种在编译时即确定结构尺寸的数据结构,即称为静态数据结构。
反之,da 则没有指定数组的边界,只是声明了数组的基类型,即数组 da 在程序运行期间只能包含整型元素,至于能包含多少元素,只能在运行期间决定。这种在运行期间决定结构尺寸的数据结构,即称为动态数据结构。
动态数组如何确定数组尺寸?
答案是:用 SetLength 这个多用途过程。SetLength 既可以用来设定字符串的长度,也可以用来设定动态数组的长度。用于设定动态数组长度的 SetLength 的声明如下:
其中,D1Length 指第一维的尺寸,D2Length 指第二维的尺寸,以此类推。
数组声明示例
varia: array of integer;ib: array of array of char;ic: array of array of array of double;beginSetLength(ia, 5);  // ia: array[0..4] of integerSetLength(ib, 3, 5);  // ib: array[0..2, 0..4] of charSetLength(ic, 2, 3, 4); // ic: array[0..1, 0..2, 0..3] of double
上例所声明的数组 ia 为一维数组,数组的边界为 0..4。注意:SetLength 中的 D1Length 是指第一维的尺寸(数组长度),即 High - Low + 1。因为动态数组的 Low 总是 0,则数组的长度 = High + 1。特别地,对于空数组,因其长度为 0,所以 High = -1。此时 High < Low。这是 Delphi 动态数组的一个非常奇特的现象。
上面的讨论也适用于多维数组,如上例中的 ib 和 ic。
由上面的例子还可以看出,多维数组的声明就是重复使用 array of 这一结构。因为动态数组类型的基本结构是:array of baseType,如果此时 baseType 是基本类型或记录等类型,则该动态数组为一维结构;如果此时 baseType 仍然是一个数组类型,即 baseType 又是一个 array of baseType,则原类型声明就成为:array of array of baseType。如果此时的 baseType 是基本类型或记录类型,则该数组为二维结构;而如果 baseType 仍然是一个 array of baseType,……。这个过程可以一直进行下去,直到声明一个 N-维 数组(N > 1)。这也是一个很有意思的现象。
注:上面的 baseType 应该标以下标,以示区别,但因为公众号编辑器不支持下标,后面附加1,2,3又很难看,所以就只能这样写了。
下面看一个例子:
由图可见,ia 和 ib 因为在同一行上声明为 array of integer,所以类型相同,可以将 ia 赋值给 ib。注意:动态数组的赋值和我们前一篇中所讲的静态数组的赋值不同,动态数组的赋值不制作副本,不拷贝,而是直接将 ia 的地址赋给 ib。如此赋值的结果,就是 ia 和 ib 指向同一个数组,因此 ia = ib 的测试结果为真。
因为 ic 不是和 ia 在同一行上声明,即使数组类型相同,但编译器认为它们不属于同一类型,不仅不能赋值,即使 Copy 也不行。这与上一篇所讨论的静态数组的情形是一样的。
将上面的代码稍微修改一下,如下图所示:
重新定义动态数组类型 array of integer 的别名 TIntArray,用这个别名来声明数组变量,这些变量就都是相同类型从而可以相互赋值和拷贝了,如上面代码所示。
另外,上面代码揭示了动态数组另一个特点。与字符串类似,动态数组也维持着引用计数,同时记录着数组的尺寸,这些结构性信息组成了 TDynArrayRec 类型,这个类型在 System.pas 中定义。为了不与 System 相冲突,我们重新定义了该类型,命名为 TDynArrayInfo。
我们上面说过,动态数组赋值是直接将源数组的地址赋给目的数组,如上面代码中所示,数组赋值将 ia 的地址赋给 ib,同时 ia 数组的引用计数增加1。因为 ic 是通过拷贝生成的新数组,所以 ic 的引用计数为1。
与字符串不同的是,数组没有“写时复制”(Copy-on-Write)的机制。如上面代码所示,在将 ia 赋值给 ib 之后,修改 ia 元素的值,ib 的值也改变了。这是因为此时 ib 并没有因 ia 要修改而复制一个新数组,仍然和 ia 共享同一个数组。
结果如下图所示:
这个结果表明:
  • ic 是通过拷贝数组 ia 从索引 1 开始的 3 个元素所组成的一个新数组,其值不受 ia 更新的影响。
  • 在修改了 ia[1] 的值之后,ib[1] 的值也同时修改了。这是因为 ia 和 ib 指向同一个数组的缘故。
  • 因为 ia 和 ib 共享同一个数组,所以 ia 的引用计数为 2,同时数组长度为 5,这正是 SetLength 所设定的长度。
  • 因为 ic 是单独的数组,所以其引用计数为 1,同时数组长度为 3,这是 Copy 时指定拷贝 3 个元素的结果。
相关阅读
Delphi 编程 -- 谈谈数组

最新文章

随机文章