LUK
LUK

Reputation: 1

delphi - how to use declare and use pointer to the const array in a const record?

I have a few const arrays of the same base type but different sizes, and I need to point to them in the const records. The code below compiles successfully, but finishes with error.

type
  Toffsets = array of integer;

  Trec = record
             point1: Tpoint;          //complete size
             point2: Tpoint;
             aOffsets:  ^Toffsets;
           end;

const
  cOffsetsA: array [0..3] of integer = (7, 4, 2, 9);
  cOffsetsB: array [0..5] of integer = (1, 2, 3, 4, 5, 6);

  cRec1: Trec = (
    point1:   (x: 140; y: 46);
    point2:   (x: 5; y: 7);
    aOffsets: @cOffsetsA;
  );

  cRec2: Trec = (
    point1:   (x: 40; y: 6);
    point2:   (x: 5; y: 7);
    aOffsets: @cOffsetsB;
  );

In my code I need to access data from the cOffsetsA/B arrays having a pointer to the record. I tried to do it like this:

var pMyRec: ^Trec;
...
pMyRec := @cRec1;
...
i := pMyRec^.aOffsets^[0];

and this causes error - 'Access violation ... read of address 000000...'

Can anybody explain is wrong here and how to fix it, how should it be done?

Probably I will also have to add the _length field in the record, which will hold the size of the array the pointer is pointing to; this is not a problem.

Best regards, LUK

Upvotes: 0

Views: 4555

Answers (3)

Wim ten Brink
Wim ten Brink

Reputation: 26682

While the use of constants like this are okay in Delphi, I just want to add that it's in general not a very good idea to do things this way. Personally, if this code is in some unit, I would use something like this: (D2007 and higher)

type
  Toffsets = array of integer;
  Trec = record
    point1: Tpoint; //complete size
    point2: Tpoint;
    aOffsets: ^Toffsets;
    constructor Create(X1, Y1, X2, Y2: Integer; Offsets: array of integer);
  end;

Thus, the record gets a constructor which will fill it with the proper data. As a disadvantage, you can't declare it as a const anymore, if you want to use the constructor to fill it with data. However, you can solve this by using the following code in the Initialization section:

var
  cRec1: Trec;

initialization
  cRec1 := Trec.Create(140, 6, 5, 7, cOffsetsA);

This code will declare the record as a global variable instead of constant, and fill it with your data.

Your constructor could look like this:

constructor Trec.Create(X1, Y1, X2, Y2: Integer; Offsets: array of integer);
var
  I: Integer;
begin
  point1.X := X1;
  point1.Y := Y1;
  point2.X := X2;
  point2.Y := Y2;
  new(aOffsets);
  SetLength(aOffsets^, 0);
  for I in Offsets do
  begin
    SetLength(aOffsets^, Succ(Length(aOffsets^)));
    aOffsets^[Pred(Length(aOffsets^))] := I;
  end;
end;

Which will fill the record with your data. However, you don't have to use the constructor to create records this way! It's just an added function.

To make it even more interesting, you can add properties for the two points and array, making the fields themselves private and only declaring read method for the properties. This way, you can still make sure it's content is read-only and stays read-only.

But what's the risk of using your code? Well, since you declare them all as constants, there isn't much risk, unless you allow assignable typed constants. ({$J+} is declared...) In that case, a piece of code could change a value in cOffsetsA and it would also change in cRec1. Is that what you want?

Anyway, by using a constructor and by initializing them in code, you make your code more dynamic. However, it still continues to stay a simple record type and the additional properties and methods won't change it's structure or size.

You will need Delphi 2007 or better for this, though...

Upvotes: 1

PhiS
PhiS

Reputation: 4650

The following should work:


i:=TOffsets(pMyRec^.aOffsets)[0];

Upvotes: 0

LUK
LUK

Reputation: 1

It looks like I found the answer myself:

type
  Toffsets = array of integer;

is a dynamic array type and setLength must be used with any variable of this type to allocate the memory. What I need is a pointer to an existing constant array, so the only thing which must be changed in order to fix the problem is the declaration of this type. It should look like:

type
  Toffsets = array [0..0] of integer;

and yes, I have to add the _length field in the records as low(pMyRec^.aOffsets^), high(...) and length(...) do not work.

Best regards, LUK

Upvotes: 0

Related Questions