5
votes

I want to create a bunch of records (RWell) and to store them in an array in a certain order. Then I want to create a new array (different layout) and rearange the records in it. Of course, I don't want to duplicate data in RAM so I though that in the second array I should put pointers to the records in the first array. However, I can't do that. Anybody can tell what's wrong with the code below? Thanks

Type 
  RWell= record                                 
    x: string;  
    i: integer;
  end;

  PWell= ^RWell;

  RWellArray= Array[0..12, 0..8] of RWell;    


procedure TClass1.CreateWells
var 
  WellMX: RWellArray;
begin 
  { should I initialize the WellXM here? }
  { note: WellXM is a static array! }

  other stuff
end;



var Wells: array of PWell;
procedure TClass2.AddWell(aWell: RWell);
begin
 aWell.Stuff:= stuff; {aWell cannot be readonly because I need to change it here}

 SetLength(Wells, Length(Wells)+ 1);  { reserve memory } 
 Wells[High(Wells)]:= @aWell;
end;



procedure TClass3.DisplayWell;
var CurWell: RWell;
begin
 CurWell:= CurPrimer.Wells[iCurWell]^;       <--- AV here (but in debugger the address is correct)
end;

Solved by Rob K.

1
You're going to get comments about your technique for appending to the end of the dynamic array. It can be inefficient and can fragment memory. You'd be better off making the array the size you'll need just once, and then assigning values to the next unassigned element as you go. Another option is to use a TList, which is very good at storing pointers. As of Delphi 2009, it could even be type-safe as TList<PWell>. - Rob Kennedy
I don't know the size of that array at run time. And I know it is not optimal ho I set its length. But it very small (below 600 entries) so the fragmentation will be minor. I won't bother change it now. Thanks for the suggestion :) - Server Overflow
I'd agree that it's better to use a list here. - Mason Wheeler

1 Answers

6
votes

In your AddWell function, you're passing the record by value. That means the function gets a copy of the actual parameter. You're storing a pointer to the formal parameter, which is probably just a location on the local stack of the function.

If you want a pointer to a well, then pass a pointer to a well:

procedure AddWell(AWell: PWell);
begin
  SetLength(Wells, Length(Wells) + 1);
  Wells[High(Wells)] := AWell;
end;

Another option is to pass the record by const value. For records, this means the actual parameter is passed as a reference. A pointer to the formal parameter is also a pointer to the actual parameter:

procedure AddWell(const AWell: RWell);
begin
  SetLength(Wells, Length(Wells) + 1);
  Wells[High(Wells)] := @AWell;
end;

I wouldn't really rely on that, though. When you want pointers, pass pointers. Some people try to avoid pointers in their code, but they're nothing to be afraid of.