Sign in Join
Wiki copytemporary

Dynamic array: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Dynamic array

OldNewLine
1+A **dynamic array** is an [[Array|array]] whose dimensions are not known at [[Compile time|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.
2+
3+## usage
4+
5+### concept
6+
7+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.
8+
9+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.
10+
11+### definition
12+
13+A one-dimensional dynamic array is defined like this:
14+
15+``` pascal
16+array of char
17+```
18+
19+Note how no dimensions’ size is specified. This is what distinguishes the definition from a normal array.
20+
21+In order to define a multidimensional array, an array itself is specified as the base type, thus creating an “array of arrays”:
22+
23+``` pascal
24+array of array of longInt
25+```
26+
27+### sizing
28+
29+The compiler [[Procedure|procedure]] [`setLength`](https://www.freepascal.org/docs-html/rtl/system/setlength.html) will change a dynamic array’s length, provided there is enough memory.
30+
31+``` pascal
32+program setLengthDemo(input, output, stdErr);
33+var
34+ sieve: array of longWord;
35+begin
36+ setLength(sieve, 1337);
37+end.
38+```
39+
40+The procedure allocates memory for as many records of the base type as specified, plus some management data. It then [*copies*](https://www.freepascal.org/docs-html/rtl/system/copyarray.html) all elements of the old incarnation to the new one. New fields that did not exist before are initialized with the [[Default|default intrinsic]].
41+
42+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.
43+
44+Multidimensional arrays can be resized with `setLength`, too, by specifying multiple sizes in the call, like this:
45+
46+``` pascal
47+program multidimensionalSetLengthDemo(input, output, stdErr);
48+var
49+ samples: array of array of smallInt;
50+begin
51+ setLength(samples, 12, 64);
52+end.
53+```
54+
55+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`.
56+
57+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,
58+
59+``` pascal
60+program binomialPotence(input, output, stdErr);
61+var
62+ pascalsTriangle: array of array of longWord;
63+ exponent: longInt;
64+ factor: longInt;
65+begin
66+ setLength(pascalsTriangle, 20);
67+
68+ setLength(pascalsTriangle[0], 1);
69+ pascalsTriangle[0][0] := 1;
70+
71+ setLength(pascalsTriangle[1], 2);
72+ pascalsTriangle[1][0] := 1;
73+ pascalsTriangle[1][1] := 1;
74+
75+ // construct values by simple addition
76+ for exponent := 2 to high(pascalsTriangle) do
77+ begin
78+ setLength(pascalsTriangle[exponent], exponent + 1);
79+ pascalsTriangle[exponent][0] := 1;
80+ pascalsTriangle[exponent][exponent] := 1;
81+ for factor := 1 to exponent - 1 do
82+ begin
83+ pascalsTriangle[exponent][factor] :=
84+ pascalsTriangle[exponent - 1][factor - 1] +
85+ pascalsTriangle[exponent - 1][factor];
86+ end;
87+ end;
88+
89+ // ...
90+```
91+
92+### initializing
93+
94+#### [[Constructor|constructor]]-like syntax
95+
96+[[FPC New Features 3.0.0|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:
97+
98+``` pascal
99+program dynamicArrayCreateDemo(input, output, stdErr);
100+type
101+ truths = array of boolean;
102+var
103+ l: truths;
104+begin
105+ l := truths.create(false, true, true, false, true, false, false);
106+end.
107+```
108+
109+Of course you can nest arrays as well:
110+
111+``` pascal
112+program nestedDynamicArrayCreateDemo(input, output, stdErr);
113+type
114+ truths = array of boolean;
115+ pattern = array of truths;
116+var
117+ p: pattern;
118+begin
119+ p := pattern.create(
120+ truths.create(false, false),
121+ truths.create(true, false),
122+ truths.create(true, false, false),
123+ truths.create(true, true, false)
124+ );
125+end.
126+```
127+
128+#### Non-Constructor like syntax
129+
130+As announced <https://lists.freepascal.org/pipermail/fpc-pascal/2018-May/053892.html> its now possible to initialize dynamic array constants and variables directly -
131+
132+``` pascal
133+
134+const
135+ Test1: array of LongInt = (1, 2, 3);
136+
137+var
138+ DArray : array of string = ('one', 'two', 'three'); // A dynamic Array
139+ IArray : array of integer = (1,2,3); // A dynamic array
140+```
141+
142+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 !
143+
144+### dynamic array literals
145+
146+[[FPC New Features 3.2.0|Since FPC 3.2.0]] it is possible to specify dynamic array literals. This works for anonymous data types as well.
147+
148+``` pascal
149+program dynamicArrayLiteralDemo(input, output, stdErr);
150+
151+procedure printArray(const list: array of integer);
152+var
153+ i: ALUSInt;
154+begin
155+ for i := low(list) to high(list) do
156+ begin
157+ writeLn(list[i]);
158+ end;
159+end;
160+
161+begin
162+ printArray([123, 456, 789]);
163+end.
164+```
165+
166+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.
167+
168+### Insert, Delete
169+
170+Dynamic Arrays have their content managed with the Insert and Delete intrinsics.
171+
172+``` pascal
173+var
174+ IArray : array of integer; // A dynamic array
175+ i : integer;
176+begin
177+ iArray := [7]; // A very small array
178+ iArray := [7, 8, 9]; // A bit bigger
179+ for i in IArray do
180+ writeln(i, ' '); // prints "7, 8, 9"
181+ writeln('');
182+ for i := 0 to 3 do
183+ insert(i, IArray, 0); // four more elements, at the start of array
184+ for i in IArray do
185+ write(i, ' '); // prints "3 2 1 0 7 8 9"
186+ delete(IArray, 0, 6); // back to a one element array, just "9"
187+end;
188+```
189+
190+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.
191+
192+### handling
193+
194+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.
195+
196+If you want to duplicate data you have to use [`system.copy`](https://www.freepascal.org/docs-html/rtl/system/copy.html).
197+
198+``` pascal
199+program dynamicArrayCopyDemo(input, output, stdErr);
200+
201+var
202+ foo, bar: array of char;
203+
204+procedure printArrays;
205+begin
206+ writeLn('foo[0] = ', foo[0], '; bar[0] = ', bar[0]);
207+end;
208+
209+begin
210+ setLength(foo, 1);
211+ foo[0] := 'X';
212+ // copies _reference_
213+ bar := foo;
214+ write(' initial values: ');
215+ printArrays;
216+
217+ // change content _via_ _second_ reference
218+ bar[0] := 'O';
219+ write('changed via 2nd ref: ');
220+ printArrays;
221+
222+ // copy content
223+ bar := copy(foo, 0, length(foo));
224+ bar[0] := 'X';
225+ write(' copied and changed: ');
226+ printArrays;
227+end.
228+```
229+
230+Only by using `copy` both arrays can be modified independently.
231+
232+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.
233+
234+Dynamic arrays are reference counted. Calling `setLength(myDynamicArrayVariable, 0)` virtually does [[Nil|myDynamicArrayVariable := nil]] and decreases the reference count. Only when the reference count hits zero, the memory block is released.
235+
236+``` pascal
237+program dynamicArrayNilDemo(input, output, stdErr);
238+var
239+ foo, bar: array of char;
240+begin
241+ setLength(foo, 1);
242+ foo[0] := 'X';
243+ // copy _reference_, increase reference count
244+ bar := foo;
245+ // foo becomes nil, reference count is decreased
246+ setLength(foo, 0);
247+ writeLn('length(foo) = ', length(foo),
248+ '; length(bar) = ', length(bar));
249+
250+ // decrease reference count another time
251+ bar := nil;
252+ writeLn('length(foo) = ', length(foo),
253+ '; length(bar) = ', length(bar));
254+end.
255+```
256+
257+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.
258+
259+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`](https://www.freepascal.org/docs-html/rtl/system/low.html) and [`high`](https://www.freepascal.org/docs-html/rtl/system/high.html) to determine valid indices (the former being optional, since dynamic arrays always start at zero). Alternatively, [[for-in loop|for … in loops]] can be used, if no index is required.
260+
261+Remember, [[SizeOf|sizeOf]] of a dynamic array evaluates to the size of a pointer.
262+
263+## application
264+
265+- there is [`system.tBoundArray`](https://www.freepascal.org/docs-html/rtl/system/tboundarray.html) and [`objPas.tBoundArray`](https://www.freepascal.org/docs-html/rtl/objpas/tboundarray.html)
266+
267+## see also
268+
269+- [[String|string]]
270+- example: [[Example: Multidimensional dynamic array|multidimensional dynamic array]]
271+- [Free Pascal Reference guide: “Dynamic arrays”](https://www.freepascal.org/docs-html/ref/refsu14.html#x38-520003.3.1)
272+
273+---
274+*From the Free Pascal wiki, page "Dynamic array", as it stood on 2022-12-14 after a change by Dbannon, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
275+Category: Code

Back to history