Difference between revisions of "Dynamic array"

From Free Pascal wiki
Jump to navigationJump to search
(warning about using TFPColors)
 
(51 intermediate revisions by 17 users not shown)
Line 1: Line 1:
The dynamic array type is a very nice feature of the freepascal syntax. It is very similar to the [[array|array]] type, but allows more flexibility for the programmer since the number of elements does not need to be known until the program execution.
+
{{Dynamic array}}
  
The declaration part is just as simple as for the [[array|array]] type: 
+
A '''dynamic array''' is an [[Array|array]] whose dimensions are not known at [[Compile time|compile-time]].
  <b>var</b>
+
The dynamic array type is not the only type providing variable-length arrays, but as of 2022 it is the only one [[FPC]] supports.
  ...
 
  MyVariable : <b>array of</b> type;
 
  ...
 
The number of elements can be set or modified whenever needed during the execution of the program by inserting the statement:
 
  <b>begin
 
  ...
 
  </b> SetLength (MyVariable, ItsNewLength);
 
  ...
 
  <b>end</b>
 
You can put as many <b>SetLength</b> statements as you want in your program in order to expand or truncate an array but you must put at least one statement before you can use the variable for the first time.  
 
  
The individual elements can be accessed as follows:
+
== usage ==
  ...
+
=== concept ===
  SetLength(MyVariable,19);
+
A dynamic array’s definition will only allocate space for a [[Pointer|pointer]].
  ...
+
During [[runtime]], various routines will ensure convenient usage but, most importantly, the syntax of accessing an array’s elements by placing indices in square brackets is supported by the [[Compiler|compiler]], implemented as automatic de-referencing of the pointer.
  MyVariable[18] := 123;
 
  ...
 
  MyOtherVariable := MyVariable[0];
 
  ...
 
  writeln('MyVariable has ',length(MyVariable),' elements');  {should be 19}
 
  ...
 
  writeln('Its range is ',low(MyVariable),' to ',high(MyVariable)); {should be 0 to 18}
 
  ...
 
  
The index of a dynamic array is ZERO based, ie. it must be within the range from 0 to (Length-1). It is NOT possible to change this to a ONE based system.
+
Dynamic arrays’ indices are always non-negative integers starting at zero for the first element.
 +
It is not possible to use an enumerative type or any other ordinal type as an index.
 +
The first element is always specified by an index of <syntaxhighlight lang="pascal" inline>0</syntaxhighlight>&nbsp;– this cannot be changed.
  
Actually, dynamic arrays are pointers with automatic dereferencing which are initialized to <b>nil</b>. This means, that <b>MyVariable</b> is interpreted as a pointer variable when handed over to low level routines like <b>fillchar</b>, <b>sizeof</b>, etc. but it is automatically expanded to <b>MyVariable^</b> when indexing its elements (as in <b>MyVariable[2]</b>) or when handed over to routines that expect array types.
+
=== definition ===
 +
A one-dimensional dynamic array is defined like this:
 +
<syntaxhighlight lang="pascal">
 +
array of char
 +
</syntaxhighlight>
 +
Note how no dimensions’ size is specified.
 +
This is what distinguishes the definition from a normal array.
  
Assigning <b>nil</b> to a dynamic variable automatically frees the memory where the pointer pointed to. It's identical to <b>SetLength(MyVariable,0)</b>. This can have a side effect, if the pointer value is not valid for some reason (i.e., if it was read from disk where it was stored from previous program runs). To init such an invalid pointer you have to use <b>fillchar(MyVariable,sizeof(MyVariable),#0)</b>.
+
In order to define a multidimensional array, an array itself is specified as the base type, thus creating an “array of arrays”:
 +
<syntaxhighlight lang="pascal">
 +
array of array of longInt
 +
</syntaxhighlight>
  
From memory management view dynamic array variables are simple pointers. <b>SetLength</b> allocates and frees memory on the heap as needed. When used in functions or procedures only the pointer is added to the stack. When the procedure exits, the dynamic array variable is removed and the computer memory is made available again. In fact, the memory management procedures are inserted in the executable program and the result is totally transparent to the programmer.
+
=== sizing ===
[[category:Pascal]]
+
The compiler [[Procedure|procedure]] {{Doc|package=RTL|unit=system|identifier=setlength|text=<syntaxhighlight lang="pascal" inline>setLength</syntaxhighlight>}} will change a dynamic array’s length, provided there is enough memory.
 +
<syntaxhighlight lang="pascal" line highlight="5">
 +
program setLengthDemo(input, output, stdErr);
 +
var
 +
sieve: array of longWord;
 +
begin
 +
setLength(sieve, 1337);
 +
end.
 +
</syntaxhighlight>
 +
The procedure allocates memory for as many records of the base type as specified, plus some management data.
 +
It then {{Doc|package=RTL|unit=system|identifier=copyarray|text=''copies''}} all elements of the old incarnation to the new one.
 +
New fields that did not exist before are initialized with the [[Default|<syntaxhighlight lang="delphi" inline>default</syntaxhighlight> intrinsic]].
 +
 
 +
It is important to remember that, since all elements are copied during <syntaxhighlight lang="pascal" inline>setLength</syntaxhighlight>, it can become extremely inefficient to use it with large arrays, or inside inner loops.
 +
 
 +
Multidimensional arrays can be resized with <syntaxhighlight lang="pascal" inline>setLength</syntaxhighlight>, too, by specifying multiple sizes in the call, like this:
 +
<syntaxhighlight lang="pascal" line highlight="5">
 +
program multidimensionalSetLengthDemo(input, output, stdErr);
 +
var
 +
samples: array of array of smallInt;
 +
begin
 +
setLength(samples, 12, 64);
 +
end.
 +
</syntaxhighlight>
 +
In this example, valid indices for <syntaxhighlight lang="pascal" inline>samples</syntaxhighlight>’ first dimension are in the range <syntaxhighlight lang="pascal" inline>0..11</syntaxhighlight>, while valid indices for its second dimension are within <syntaxhighlight lang="pascal" inline>0..63</syntaxhighlight>.
 +
 
 +
One quite useful fact is that, because multidimensional dynamic arrays are always “arrays of arrays”, the limitation that all dimensions must be of the same size does not apply to them.
 +
Different dimensions are implemented as arrays, and can each have their own size.
 +
For instance,
 +
<syntaxhighlight lang="pascal" line highlight="7,9,12,19">
 +
program binomialPotence(input, output, stdErr);
 +
var
 +
pascalsTriangle: array of array of longWord;
 +
exponent: longInt;
 +
factor: longInt;
 +
begin
 +
setLength(pascalsTriangle, 20);
 +
 +
setLength(pascalsTriangle[0], 1);
 +
pascalsTriangle[0][0] := 1;
 +
 +
setLength(pascalsTriangle[1], 2);
 +
pascalsTriangle[1][0] := 1;
 +
pascalsTriangle[1][1] := 1;
 +
 +
// construct values by simple addition
 +
for exponent := 2 to high(pascalsTriangle) do
 +
begin
 +
setLength(pascalsTriangle[exponent], exponent + 1);
 +
pascalsTriangle[exponent][0] := 1;
 +
pascalsTriangle[exponent][exponent] := 1;
 +
for factor := 1 to exponent - 1 do
 +
begin
 +
pascalsTriangle[exponent][factor] :=
 +
pascalsTriangle[exponent - 1][factor - 1] +
 +
pascalsTriangle[exponent - 1][factor];
 +
end;
 +
end;
 +
 +
// ...
 +
</syntaxhighlight>
 +
 
 +
=== initializing ===
 +
==== [[Constructor|<syntaxhighlight lang="delphi" inline>constructor</syntaxhighlight>]]-like syntax ====
 +
[[FPC New Features 3.0.0#Dynamic array constructors|Since FPC 3.0.0]] dynamic array types that are not anonymous are automatically equipped with a “constructor” as it might be familiar from object-oriented programming.
 +
This lets you unite <syntaxhighlight lang="pascal" inline>setLength</syntaxhighlight> calls and a series of assignments in one statement:
 +
<syntaxhighlight lang="pascal" line highlight="7">
 +
program dynamicArrayCreateDemo(input, output, stdErr);
 +
type
 +
truths = array of boolean;
 +
var
 +
l: truths;
 +
begin
 +
l := truths.create(false, true, true, false, true, false, false);
 +
end.
 +
</syntaxhighlight>
 +
Of course you can nest arrays as well:
 +
<syntaxhighlight lang="pascal" line highlight="8-13">
 +
program nestedDynamicArrayCreateDemo(input, output, stdErr);
 +
type
 +
truths = array of boolean;
 +
pattern = array of truths;
 +
var
 +
p: pattern;
 +
begin
 +
p := pattern.create(
 +
truths.create(false, false),
 +
truths.create(true, false),
 +
truths.create(true, false, false),
 +
truths.create(true, true, false)
 +
);
 +
end.
 +
</syntaxhighlight>
 +
 
 +
==== Non-Constructor like syntax ====
 +
As announced https://lists.freepascal.org/pipermail/fpc-pascal/2018-May/053892.html its now possible to initialize dynamic array constants and variables directly -
 +
<syntaxhighlight lang="pascal" >
 +
 
 +
const
 +
  Test1: array of LongInt = (1, 2, 3);
 +
 
 +
var
 +
    DArray : array of string  = ('one', 'two', 'three');  // A dynamic Array
 +
    IArray : array of integer = (1,2,3);                  // A dynamic array  </syntaxhighlight> 
 +
This is available in at least FPC 3.2.2. A very minor note, the above will not work with the predefined FPC colors such as colRed and colBlue because the TFCColors are Typed Constants. I bet you needed to know that !
 +
 
 +
=== dynamic array literals ===
 +
[[FPC New Features 3.2.0#Dynamic Array constants and variable initialization|Since FPC 3.2.0]] it is possible to specify dynamic array literals.
 +
This works for anonymous data types as well.
 +
<syntaxhighlight lang="pascal" line highlight="3,14">
 +
program dynamicArrayLiteralDemo(input, output, stdErr);
 +
 
 +
procedure printArray(const list: array of integer);
 +
var
 +
i: ALUSInt;
 +
begin
 +
for i := low(list) to high(list) do
 +
begin
 +
writeLn(list[i]);
 +
end;
 +
end;
 +
 
 +
begin
 +
printArray([123, 456, 789]);
 +
end.</syntaxhighlight>
 +
Note that the data type of the formal parameter <syntaxhighlight lang="pascal" inline>list</syntaxhighlight> does not have a name, yet we are able to specify a dynamic <syntaxhighlight lang="pascal" inline>array of integer</syntaxhighlight> literal as an actual parameter.
 +
 
 +
=== Insert, Delete ===
 +
 
 +
Dynamic Arrays have their content managed with the Insert and Delete intrinsics.
 +
 
 +
<syntaxhighlight  lang="pascal" >
 +
var
 +
    IArray  : array of integer;                    // A dynamic array
 +
    i : integer;
 +
begin
 +
    iArray := [7];                                // A very small array
 +
    iArray := [7, 8, 9];                          // A bit bigger
 +
    for i in IArray do
 +
        writeln(i, ' ');                          // prints "7, 8, 9"
 +
    writeln('');
 +
    for i := 0 to 3 do
 +
        insert(i, IArray, 0);                      // four more elements, at the start of array
 +
    for i in IArray do
 +
        write(i, ' ');                            // prints "3 2 1 0 7 8 9"
 +
    delete(IArray, 0, 6);                          // back to a one element array, just "9"
 +
end;</syntaxhighlight>
 +
 
 +
Used this way, the compiler generates code to increase and decrease the memory allocated for the array, while this is convenient, it may not be best for large data sets or the situations that need the fastest response.
 +
 
 +
=== handling ===
 +
Keep in mind dynamic arrays are pointers.
 +
Assigning dynamic array variables to each other does not copy any payload, but just the address.
 +
This differs from static arrays’ behavior.
 +
 
 +
If you want to duplicate data you have to use {{Doc|package=RTL|unit=system|identifier=copy|text=<syntaxhighlight lang="pascal" inline>system.copy</syntaxhighlight>}}.
 +
<syntaxhighlight lang="pascal" line highlight="25">
 +
program dynamicArrayCopyDemo(input, output, stdErr);
 +
 
 +
var
 +
foo, bar: array of char;
 +
 
 +
procedure printArrays;
 +
begin
 +
writeLn('foo[0] = ', foo[0], '; bar[0] = ', bar[0]);
 +
end;
 +
 
 +
begin
 +
setLength(foo, 1);
 +
foo[0] := 'X';
 +
// copies _reference_
 +
bar := foo;
 +
write('    initial values: ');
 +
printArrays;
 +
 +
// change content _via_ _second_ reference
 +
bar[0] := 'O';
 +
write('changed via 2nd ref: ');
 +
printArrays;
 +
 +
// copy content
 +
bar := copy(foo, 0, length(foo));
 +
bar[0] := 'X';
 +
write(' copied and changed: ');
 +
printArrays;
 +
end.
 +
</syntaxhighlight>
 +
Only by using <syntaxhighlight lang="pascal" inline>copy</syntaxhighlight> both arrays can be modified independently.
 +
 
 +
As stated above, <syntaxhighlight lang="pascal" inline>setLength</syntaxhighlight> ''copies'' data.
 +
This procedure can be used to make data unique again, despite the fact only the references, the pointers (so just addresses) have been copied (<syntaxhighlight lang="pascal" inline>bar := foo</syntaxhighlight>).
 +
The highlighted line in the example above could be replaced by <syntaxhighlight lang="pascal" inline>setLength(bar, length(bar))</syntaxhighlight>, though the <syntaxhighlight lang="pascal" inline>copy</syntaxhighlight> function is preferred as it is more explanatory.
 +
 
 +
Dynamic arrays are reference counted.
 +
Calling <syntaxhighlight lang="pascal" inline>setLength(myDynamicArrayVariable, 0)</syntaxhighlight> virtually does [[Nil|<syntaxhighlight lang="pascal" inline>myDynamicArrayVariable := nil</syntaxhighlight>]] and decreases the reference count.
 +
Only when the reference count hits zero, the memory block is released.
 +
<syntaxhighlight lang="pascal" line highlight="9,10,15">
 +
program dynamicArrayNilDemo(input, output, stdErr);
 +
var
 +
foo, bar: array of char;
 +
begin
 +
setLength(foo, 1);
 +
foo[0] := 'X';
 +
// copy _reference_, increase reference count
 +
bar := foo;
 +
// foo becomes nil, reference count is decreased
 +
setLength(foo, 0);
 +
writeLn('length(foo) = ', length(foo),
 +
'; length(bar) = ', length(bar));
 +
 +
// decrease reference count another time
 +
bar := nil;
 +
writeLn('length(foo) = ', length(foo),
 +
'; length(bar) = ', length(bar));
 +
end.
 +
</syntaxhighlight>
 +
Nonetheless, dynamic arrays are finalized automatically.
 +
It is not necessary to manually <syntaxhighlight lang="pascal" inline>setLength(…, 0)</syntaxhighlight> on all your references when the program comes to end, or when leaving a scope in general.
 +
 
 +
Without <syntaxhighlight lang="pascal" inline>{$rangeChecks on}</syntaxhighlight> it is possible to reach beyond an array’s limits.
 +
That means when iterating over dynamic arrays, it is impossible to work without {{Doc|package=RTL|unit=system|identifier=low|text=<syntaxhighlight lang="pascal" inline>low</syntaxhighlight>}} and {{Doc|package=RTL|unit=system|identifier=high|text=<syntaxhighlight lang="pascal" inline>high</syntaxhighlight>}} to determine valid indices (the former being optional, since dynamic arrays always start at zero).
 +
Alternatively, [[for-in loop|<syntaxhighlight lang="pascal" inline>for … in</syntaxhighlight> loops]] can be used, if no index is required.
 +
 
 +
Remember, [[SizeOf#dynamic arrays and alike|<syntaxhighlight lang="pascal" inline>sizeOf</syntaxhighlight>]] of a dynamic array evaluates to the size of a pointer.
 +
 
 +
== application ==
 +
* there is {{Doc|package=RTL|unit=system|identifier=tboundarray|text=<syntaxhighlight lang="pascal" inline>system.tBoundArray</syntaxhighlight>}} and {{Doc|package=RTL|unit=objpas|identifier=tboundarray|text=<syntaxhighlight lang="pascal" inline>objPas.tBoundArray</syntaxhighlight>}}
 +
 
 +
== see also ==
 +
* [[String|<syntaxhighlight lang="pascal" inline>string</syntaxhighlight>]]
 +
* example: [[Example: Multidimensional dynamic array|multidimensional dynamic array]]
 +
* [https://www.freepascal.org/docs-html/ref/refsu14.html#x38-520003.3.1 Free Pascal Reference guide: “Dynamic arrays”]
 +
 
 +
[[Category: Code]]

Latest revision as of 05:07, 14 December 2022

English (en) español (es) suomi (fi) français (fr) 日本語 (ja) русский (ru)

A dynamic array is an array whose dimensions are not known at compile-time. The dynamic array type is not the only type providing variable-length arrays, but as of 2022 it is the only one FPC supports.

usage

concept

A dynamic array’s definition will only allocate space for a pointer. During runtime, various routines will ensure convenient usage but, most importantly, the syntax of accessing an array’s elements by placing indices in square brackets is supported by the compiler, implemented as automatic de-referencing of the pointer.

Dynamic arrays’ indices are always non-negative integers starting at zero for the first element. It is not possible to use an enumerative type or any other ordinal type as an index. The first element is always specified by an index of 0 – this cannot be changed.

definition

A one-dimensional dynamic array is defined like this:

array of char

Note how no dimensions’ size is specified. This is what distinguishes the definition from a normal array.

In order to define a multidimensional array, an array itself is specified as the base type, thus creating an “array of arrays”:

array of array of longInt

sizing

The compiler procedure setLength will change a dynamic array’s length, provided there is enough memory.

1program setLengthDemo(input, output, stdErr);
2var
3	sieve: array of longWord;
4begin
5	setLength(sieve, 1337);
6end.

The procedure allocates memory for as many records of the base type as specified, plus some management data. It then copies all elements of the old incarnation to the new one. New fields that did not exist before are initialized with the default intrinsic.

It is important to remember that, since all elements are copied during setLength, it can become extremely inefficient to use it with large arrays, or inside inner loops.

Multidimensional arrays can be resized with setLength, too, by specifying multiple sizes in the call, like this:

1program multidimensionalSetLengthDemo(input, output, stdErr);
2var
3	samples: array of array of smallInt;
4begin
5	setLength(samples, 12, 64);
6end.

In this example, valid indices for samples’ first dimension are in the range 0..11, while valid indices for its second dimension are within 0..63.

One quite useful fact is that, because multidimensional dynamic arrays are always “arrays of arrays”, the limitation that all dimensions must be of the same size does not apply to them. Different dimensions are implemented as arrays, and can each have their own size. For instance,

 1program binomialPotence(input, output, stdErr);
 2var
 3	pascalsTriangle: array of array of longWord;
 4	exponent: longInt;
 5	factor: longInt;
 6begin
 7	setLength(pascalsTriangle, 20);
 8	
 9	setLength(pascalsTriangle[0], 1);
10	pascalsTriangle[0][0] := 1;
11	
12	setLength(pascalsTriangle[1], 2);
13	pascalsTriangle[1][0] := 1;
14	pascalsTriangle[1][1] := 1;
15	
16	// construct values by simple addition
17	for exponent := 2 to high(pascalsTriangle) do
18	begin
19		setLength(pascalsTriangle[exponent], exponent + 1);
20		pascalsTriangle[exponent][0] := 1;
21		pascalsTriangle[exponent][exponent] := 1;
22		for factor := 1 to exponent - 1 do
23		begin
24			pascalsTriangle[exponent][factor] :=
25				pascalsTriangle[exponent - 1][factor - 1] +
26				pascalsTriangle[exponent - 1][factor];
27		end;
28	end;
29	
30	// ...

initializing

constructor-like syntax

Since FPC 3.0.0 dynamic array types that are not anonymous are automatically equipped with a “constructor” as it might be familiar from object-oriented programming. This lets you unite setLength calls and a series of assignments in one statement:

1program dynamicArrayCreateDemo(input, output, stdErr);
2type
3	truths = array of boolean;
4var
5	l: truths;
6begin
7	l := truths.create(false, true, true, false, true, false, false);
8end.

Of course you can nest arrays as well:

 1program nestedDynamicArrayCreateDemo(input, output, stdErr);
 2type
 3	truths = array of boolean;
 4	pattern = array of truths;
 5var
 6	p: pattern;
 7begin
 8	p := pattern.create(
 9			truths.create(false, false),
10			truths.create(true, false),
11			truths.create(true, false, false),
12			truths.create(true, true, false)
13		);
14end.

Non-Constructor like syntax

As announced https://lists.freepascal.org/pipermail/fpc-pascal/2018-May/053892.html its now possible to initialize dynamic array constants and variables directly -

const
   Test1: array of LongInt = (1, 2, 3);

var
    DArray : array of string  = ('one', 'two', 'three');  // A dynamic Array
    IArray : array of integer = (1,2,3);                  // A dynamic array

This is available in at least FPC 3.2.2. A very minor note, the above will not work with the predefined FPC colors such as colRed and colBlue because the TFCColors are Typed Constants. I bet you needed to know that !

dynamic array literals

Since FPC 3.2.0 it is possible to specify dynamic array literals. This works for anonymous data types as well.

 1program dynamicArrayLiteralDemo(input, output, stdErr);
 2
 3procedure printArray(const list: array of integer);
 4var
 5	i: ALUSInt;
 6begin
 7	for i := low(list) to high(list) do
 8	begin
 9		writeLn(list[i]);
10	end;
11end;
12
13begin
14	printArray([123, 456, 789]);
15end.

Note that the data type of the formal parameter list does not have a name, yet we are able to specify a dynamic array of integer literal as an actual parameter.

Insert, Delete

Dynamic Arrays have their content managed with the Insert and Delete intrinsics.

var
    IArray  : array of integer;                    // A dynamic array
    i : integer;
begin
    iArray := [7];                                 // A very small array
    iArray := [7, 8, 9];                           // A bit bigger
    for i in IArray do
        writeln(i, ' ');                           // prints "7, 8, 9"
    writeln('');
    for i := 0 to 3 do
        insert(i, IArray, 0);                      // four more elements, at the start of array
    for i in IArray do
        write(i, ' ');                             // prints "3 2 1 0 7 8 9"
    delete(IArray, 0, 6);                          // back to a one element array, just "9" 
end;

Used this way, the compiler generates code to increase and decrease the memory allocated for the array, while this is convenient, it may not be best for large data sets or the situations that need the fastest response.

handling

Keep in mind dynamic arrays are pointers. Assigning dynamic array variables to each other does not copy any payload, but just the address. This differs from static arrays’ behavior.

If you want to duplicate data you have to use system.copy.

 1program dynamicArrayCopyDemo(input, output, stdErr);
 2
 3var
 4	foo, bar: array of char;
 5
 6procedure printArrays;
 7begin
 8	writeLn('foo[0] = ', foo[0], '; bar[0] = ', bar[0]);
 9end;
10
11begin
12	setLength(foo, 1);
13	foo[0] := 'X';
14	// copies _reference_
15	bar := foo;
16	write('     initial values: ');
17	printArrays;
18	
19	// change content _via_ _second_ reference
20	bar[0] := 'O';
21	write('changed via 2nd ref: ');
22	printArrays;
23	
24	// copy content
25	bar := copy(foo, 0, length(foo));
26	bar[0] := 'X';
27	write(' copied and changed: ');
28	printArrays;
29end.

Only by using copy both arrays can be modified independently.

As stated above, setLength copies data. This procedure can be used to make data unique again, despite the fact only the references, the pointers (so just addresses) have been copied (bar := foo). The highlighted line in the example above could be replaced by setLength(bar, length(bar)), though the copy function is preferred as it is more explanatory.

Dynamic arrays are reference counted. Calling setLength(myDynamicArrayVariable, 0) virtually does myDynamicArrayVariable := nil and decreases the reference count. Only when the reference count hits zero, the memory block is released.

 1program dynamicArrayNilDemo(input, output, stdErr);
 2var
 3	foo, bar: array of char;
 4begin
 5	setLength(foo, 1);
 6	foo[0] := 'X';
 7	// copy _reference_, increase reference count
 8	bar := foo;
 9	// foo becomes nil, reference count is decreased
10	setLength(foo, 0);
11	writeLn('length(foo) = ', length(foo),
12		'; length(bar) = ', length(bar));
13	
14	// decrease reference count another time
15	bar := nil;
16	writeLn('length(foo) = ', length(foo),
17		'; length(bar) = ', length(bar));
18end.

Nonetheless, dynamic arrays are finalized automatically. It is not necessary to manually setLength(, 0) on all your references when the program comes to end, or when leaving a scope in general.

Without {$rangeChecks on} it is possible to reach beyond an array’s limits. That means when iterating over dynamic arrays, it is impossible to work without low and high to determine valid indices (the former being optional, since dynamic arrays always start at zero). Alternatively, for in loops can be used, if no index is required.

Remember, sizeOf of a dynamic array evaluates to the size of a pointer.

application

see also