## 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

## Bob said

Could you post a example of how to use the unit?

I can’t find hot to create/use the DynamicDoubleArray array.

Var

tt : TDynamicDoubleArray;

Begin

tt.Create(1,10);

End;

compiler do not complain, but crash when executing the procedure.

How to create, dimension, put/get values from the array…?

## jpluimers said

Sorry Bob, this is not going to happen any time soon.

I first need to recover from metastasized rectum cancer, which will take considerable time (think quarters, not weeks).

Until then, the blog is on “auto-post” (with a queue of about 18 months).

Health status updates will be at https://twitter.com/jpluimers

Regards,

–jeroen

## Bob said

Could you post an example using the unit?

I have been looking for this long time ago, but can’t find how to create the DynamicDoubleArray array and how to get / set values.

## 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.

## Bob said

Lower zero bound is useful when you have to translate large code where the arrays are 1-based lower bounds, like in Fortran. Code is so large that is preferable to keep the structure as the code is.

In my example, it includes complex math iterations that could lead to wrong results if modifying the bounds for zero based, unless you spend lots of extra time to fully understand the operation and re-do the J-1 things properly…

IMHO

## Bob said

Lower non-zero bound is useful when you have to translate large code where the arrays are 1-based lower bounds, like in Fortran. Code is so large that is preferable to keep the structure as the code is.

In my example, it includes complex math iterations that could lead to wrong results if modifying the bounds for zero based, unless you spend lots of extra time to fully understand the operation and re-do the J-1 things properly…

IMHO

## jpluimers said

I totally agree with this. Please remind me at year end, maybe I’ve recovered enough by then to create some proper examples.

Note that Rudy has passed away last year, so he won’t respond.