## Delphi history: No, dynamic arrays do not support a non-zero lower bound, but what if you want them? – via Stack Overflow

Posted by jpluimers on 2017/11/21

A post of some older Delphi stuff I did in the past just in case need it again.

David Heffernan found the documentation for this: [WayBack] Structured Types (Delphi): Dynamic Arrays – RAD Studio

Since I needed a dynamic array structure supporting a non-zero lower bound, I was glad he also provided an answer with a data structure that does provide a non-zero lower bound.

For my own reference I’ve put his answers and questions below (as it’s way easier to search my blog than the complete internet) and my own implementation:

## Answers:

## Lower bound of dynamic arrays

Dynamic arrays always have a lower bound of

`0`

. So,`low(A)`

equals`0`

for all dynamic arrays. This is even true for empty dynamic arrays, i.e.`nil`

. From the documentation:Dynamic arrays are always integer-indexed, always starting from 0.

`TSpecifiedBoundsArray`

Having answered your direct question already, I also offer you the beginnings of a generic class that you can use in your porting.

`type TSpecifiedBoundsArray<T> = class private FValues: TArray<T>; FLow: Integer; function GetHigh: Integer; procedure SetHigh(Value: Integer); function GetLength: Integer; procedure SetLength(Value: Integer); function GetItem(Index: Integer): T; procedure SetItem(Index: Integer; const Value: T); public property Low: Integer read FLow write FLow; property High: Integer read GetHigh write SetHigh; property Length: Integer read GetLength write SetLength; property Items[Index: Integer]: T read GetItem write SetItem; default; end; { TSpecifiedBoundsArray<T> } function TSpecifiedBoundsArray<T>.GetHigh: Integer; begin Result := FLow+System.High(FValues); end; procedure TSpecifiedBoundsArray<T>.SetHigh(Value: Integer); begin SetLength(FValues, 1+Value-FLow); end; function TSpecifiedBoundsArray<T>.GetLength: Integer; begin Result := System.Length(FValues); end; procedure TSpecifiedBoundsArray<T>.SetLength(Value: Integer); begin System.SetLength(FValues, Value); end; function TSpecifiedBoundsArray<T>.GetItem(Index: Integer): T; begin Result := FValues[Index-FLow]; end; function TSpecifiedBoundsArray<T>.SetItem(Index: Integer; const Value: T); begin FValues[Index-FLow] := Value; end;`

I think it’s pretty obvious how this works. I contemplated using a

`record`

but I consider that to be unworkable. That’s down to the mix between value type semantics for

`FLow`

and reference type semantics for`FValues`

. So, I think a class is best here. It also behaves rather weirdly when you modify`Low`

. No doubt you’d want to extend this. You’d add a`SetBounds`

, a “copy to”, a “copy from” and so on. But I think you may find it useful. It certainly shows how you can make an object that looks very much like an array with non-zero lower bound.## Question:

## Do dynamic arrays support a non-zero lower bound (for VarArrayCreate compatibility)?

I’m going maintain and port to Delphi XE2 a bunch of very old Delphi code that is full of VarArrayCreate constructs to fake dynamic arrays having a lower bound that is not zero. Drawbacks of using Variant types are:

- quite a bit slower than native arrays (the code does a lot of complex financial calculations, so speed is important)
- not type safe (especially when by accident a wrong
`var...`

constant is used, and the Variant system starts to do unwanted conversions or rounding)Both could become moot if I could use dynamic arrays. Good thing about variant arrays

is that they can have non-zero lower bounds. What I recollect is that dynamic arrays used to always start at a lower bound of zero. Is this still true? In other words:Is it possible to have dynamic arrays start at a different bound than zero?

As an illustration a before/after example for a specific case (single dimensional, but the code is full of multi-dimensional arrays, and besides varDouble, the code also uses various other`varXXX`

data types that TVarData allows to use):`function CalculateVector(aSV: TStrings): Variant; var I: Integer; begin Result := VarArrayCreate([1,aSV.Count-1],varDouble); for I := 1 to aSV.Count-1 do Result[I] := CalculateItem(aSV, I); end;`

The

`CalculateItem`

function returns`Double`

. Bounds are from`1`

to`aSV.Count-1`

. Current replacement is like this, trading

the space zeroth element of Result for improved compile time checking:`type TVector = array of Double; function CalculateVector(aSV: TStrings): TVector; var I: Integer; begin SetLength(Result, aSV.Count); // lower bound is zero, we start at 1 so we ignore the zeroth element for I := 1 to aSV.Count-1 do Result[I] := CalculateItem(aSV, I); end;`

### My own implementation:

unit DynamicArrays; interface type IDynamicDoubleArray = interface function GetCount: Integer; function GetLowerBound: Integer; function GetRelativeIndex(const Index: Integer): Integer; function GetUpperBound: Integer; function GetValue(Index: Integer): Double; procedure ReDimension(const UpperBound: Integer); procedure SetValue(Index: Integer; const Value: Double); property Count: Integer read GetCount; property LowerBound: Integer read GetLowerBound; property UpperBound: Integer read GetUpperBound; property Value[Index: Integer]: Double read GetValue write SetValue; default; end; TDynamicDoubleArray = class(TInterfacedObject, IDynamicDoubleArray) strict private FLowerBound: Integer; FStorage: array of Double; FUpperBound: Integer; protected function GetCount: Integer; virtual; function GetLowerBound: Integer; virtual; function GetRelativeIndex(const Index: Integer): Integer; virtual; function GetUpperBound: Integer; virtual; function GetValue(Index: Integer): Double; virtual; procedure ReDimension(const UpperBound: Integer); virtual; procedure SetUpperBoundAndReAllocateStorage(const UpperBound: Integer); virtual; procedure SetValue(Index: Integer; const Value: Double); virtual; property Count: Integer read GetCount; property LowerBound: Integer read GetLowerBound; property UpperBound: Integer read GetUpperBound; property Value[Index: Integer]: Double read GetValue write SetValue; default; public constructor Create(const UpperBound: Integer); overload; constructor Create(const LowerBound, UpperBound: Integer); overload; end; implementation uses Variants, VarUtils, SysUtils, SysConst; constructor TDynamicDoubleArray.Create(const UpperBound: Integer); begin Create(0, UpperBound); end; constructor TDynamicDoubleArray.Create(const LowerBound, UpperBound: Integer); begin FLowerBound := LowerBound; SetUpperBoundAndReAllocateStorage(UpperBound); end; function TDynamicDoubleArray.GetCount: Integer; begin Result := 1 + UpperBound - LowerBound; // 2..5 -> 1 + 5 - 2 == 4 end; function TDynamicDoubleArray.GetLowerBound: Integer; begin Result := FLowerBound; end; function TDynamicDoubleArray.GetUpperBound: Integer; begin Result := FUpperBound; end; function TDynamicDoubleArray.GetValue(Index: Integer): Double; var RelativeIndex: Integer; begin RelativeIndex := GetRelativeIndex(Index); Result := FStorage[RelativeIndex]; end; procedure TDynamicDoubleArray.SetValue(Index: Integer; const Value: Double); var RelativeIndex: Integer; begin RelativeIndex := GetRelativeIndex(Index); FStorage[RelativeIndex] := Value; end; { IDynamicDoubleArray } function TDynamicDoubleArray.GetRelativeIndex(const Index: Integer): Integer; begin if Index > UpperBound then raise ERangeError.CreateRes(@SRangeError); if Index < LowerBound then raise ERangeError.CreateRes(@SRangeError); Result := Index - LowerBound; end; procedure TDynamicDoubleArray.ReDimension(const UpperBound: Integer); begin SetUpperBoundAndReAllocateStorage(UpperBound); end; { IDynamicDoubleArray } procedure TDynamicDoubleArray.SetUpperBoundAndReAllocateStorage(const UpperBound: Integer); begin if UpperBound < LowerBound then raise ERangeError.CreateRes(@SRangeError); FUpperBound := UpperBound; SetLength(FStorage, Count); end; end.

–jeroen

via: [WayBack] delphi – Do dynamic arrays support a non-zero lower bound (for VarArrayCreate compatibility)? – Stack Overflow

## rgreat said

Why make it class and not a record?

TArrayEx = record

## jpluimers said

Because I wanted a reference type, not a value type:

## Rudy Velthuis said

A record should work fine and would be usable like a value type. But why on earth would anyone

needa non-zero lower bound? You may want one, but there is no need for them.## jpluimers said

In for instance the actuary profession, they are very common. Code like this makes it a lot easier to translate business requests into code.