Pascal & Object Pascal DISCUSSION

Pascal dynamic arrays: why does changing a copy change the original, and how does SetLength behave?

Started by prabhakaran Pascal dynamic arraysSetLengthreference countingCopy functionarray growth strategy
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Pascal dynamic arrays: why does changing a copy change the original, and how does SetLength behave?

prabhakaran Pascal & Object Pascal Forum
#1

I keep ADC samples in Samples: array of Integer. To filter them I wrote Work := Samples; and modified Work, and found the original data changed as well. With strings the same kind of assignment never affected the original. I also build the array by calling SetLength(Samples, Length(Samples) + 1) for each new sample, and I need to delete elements from the middle.

How do assignment, SetLength and deletion really work for dynamic arrays, and what is the efficient way to grow one?

Community replies 5

Re: Pascal dynamic arrays: why does changing a copy change the original, and how does SetLength behave?

#2

A dynamic array variable is a reference to a reference-counted block on the heap. Work := Samples copies the reference and raises the count; both names now denote the same elements, so Work[0] := 99 changes Samples[0]. Strings look similar but are copy-on-write: the first modification of a shared string makes a private copy. Dynamic arrays have no copy-on-write for element assignment.

To get an independent array, write Work := Copy(Samples);. Static arrays such as array[0..99] of Integer are plain values and are copied in full by assignment.

Re: Pascal dynamic arrays: why does changing a copy change the original, and how does SetLength behave?

#3

SetLength allocates or reallocates the block. Existing elements are preserved up to the new length, new elements are zero-initialised, and indices always run from 0 to Length - 1, which is High(Samples). It also guarantees that the variable ends up with a block of its own: if the array was shared, SetLength copies it. SetLength(Samples, 0) or Samples := nil releases the memory.

For two dimensions, SetLength(M, 3, 4) creates 3 rows of 4 elements. Each row is its own dynamic array, so rows can have different lengths and the 12 elements are not one contiguous block.

Re: Pascal dynamic arrays: why does changing a copy change the original, and how does SetLength behave?

#4

Growing by one element per sample is the costly part. Each SetLength may have to move the whole block, so appending N samples one at a time can copy in the order of N²/2 elements in the worst case: for 100,000 samples that is about 5 × 10^9 element moves. Memory managers can often extend a block in place, which hides the problem until the heap becomes fragmented.

Keep a separate count and grow the capacity geometrically: when Count = Length(Samples), call SetLength(Samples, Length(Samples) * 2 + 16), and trim to Count at the end. That needs only 13 reallocations to pass 100,000 elements. A generic TList<Integer> does the same bookkeeping for you.

Re: Pascal dynamic arrays: why does changing a copy change the original, and how does SetLength behave?

#5

To delete element i from an array of a simple type, shift the tail down and shorten: Move(A[i + 1], A[i], (Length(A) - i - 1) * SizeOf(A[0])); SetLength(A, Length(A) - 1);. Deleting index 1 from five integers moves 3 elements, 12 bytes. Skip the Move when i is the last index, since A[i + 1] is then out of range.

Do not use a raw Move like this on arrays of strings, interfaces or other managed types: it duplicates and overwrites references without updating their counts, which leads to leaks or double frees. Use a loop of normal assignments, or the built-in Delete(A, i, 1) and Insert for dynamic arrays that recent Delphi and Free Pascal versions provide.

Re: Pascal dynamic arrays: why does changing a copy change the original, and how does SetLength behave?

#6

Two safeguards help while you sort this out. Compile with range checking on, {$R+}, during development: indexing past High(A) on a dynamic array is otherwise silent memory corruption. An empty array has High(A) = -1, so for i := 0 to High(A) correctly runs zero times.

And when passing the array to a routine that must not alter your data, remember the reference semantics described above: pass Copy(Samples), or have the routine build and return a new array as its result.

TEP COMMUNITY