Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-10

Why use pointers to arrays?

11 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Graeme Geldenhuys

Hi,

The other "arrays" discussion got me thinking, and after working so long with only desktop type applications that talk to database API's, I long some knowledge of the lower lever Pascal features that was so often used in Turbo Pascal days.

Anyway, I'm port an application that was written for OS/2 years ago using SpeedPascal. I have already found quite a differences between Free Pascal and SpeedPascal implementations of the "object pascal" language.

Anyway to get to my questions, and using the code snippets shown below. This code is all based on the code I am porting from SpeedPascal. So maybe some strange usage is simply due to the age of SpeedPascal etc..

----------------------
Type
  TLayoutLine = record
    Text: PChar;
    Length: longint;
    Height: longint;
    Width: longint;
    MaxDescender: longint;
    MaxTextHeight: longint;
    LinkIndex: longint;
    Style: TTextDrawStyle;
    Wrapped: boolean;
  end;
  TLinesArray = array[ 0..0 ] of TLayoutLine;
--------------------------

1) Why would you use a pointer to an array and not simply a variable what IS the array? In the program I am porting the following field variable is defined.

  FLines: ^TLinesArray;

Why couldn't I simply say:

  FLines: TLinesArray;

Is there some advantage for using a pointer to the array, when passing it around between procedures and functions?

2) The TLinesArray is a dynamic array, so shouldn't I maybe change the ported code to define the array as follows, instead of the [0..0] idea?

  TLinesArray = array of TLayoutLine;

3) Maybe the strange usage (to me at least) of dynamic arrays could be explain in how the original program allocates memory for the array. In the class where FLines is defined, it initially reserves ten empty elements for the array in the class constructor.

  FAllocatedNumLines := 10;
  GetMem( FLines, FAllocatedNumLines * sizeof( TLayoutLine ) );

And then later if it needs more space for elements, it redefines the array elements as follows:

Procedure TRichTextLayout.AddLineStart( Const Line: TLayoutLine );
var
  NewAllocation: longint;
begin
  if FNumLines >= FAllocatedNumLines then
  begin
    // reallocate the array twice the size
    NewAllocation := FAllocatedNumLines * 2;
    FLines := ReAllocMem(FLines, NewAllocation * sizeof(TLayoutLine));
    FAllocatedNumLines := NewAllocation;
  end;
  FLines^[ FNumLines ] := Line;    //  ***** (1)
  inc( FNumLines );
end;

Like I said, maybe this is related to different implementations of object pascal. By doesn't one allocate memory (array elements) via the SetLength() procedure?
eg:

  FAllocatedNumLines := 10;
  SetLength( FLines^, FAllocatedNumLines);

I marked on of the lines above with (1). I sometimes get "out of range" errors there, so clearly somewhere in all this dereferencing of arrays etc, I probably made a mistake somewhere in porting/translating the SpeedPascal code to Free Pascal.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/
Florian Klaempfl
to Graeme Geldenhuys
Show quoted text 11 lines
1) Why would you use a pointer to an array and not simply a variable what IS the array? In the program I am porting the following field variable is defined. FLines: ^TLinesArray; Why couldn't I simply say: FLines: TLinesArray;

Because this array has a fixed size.

Show quoted text 10 lines
Is there some advantage for using a pointer to the array, when passing it around between procedures and functions? 2) The TLinesArray is a dynamic array, so shouldn't I maybe change the ported code to define the array as follows, instead of the [0..0] idea? TLinesArray = array of TLayoutLine;

A dynamic array does several magic behind your neck mainly taking care of managed types. It depends on the code if it works or not.

Show quoted text 33 lines
3) Maybe the strange usage (to me at least) of dynamic arrays could be explain in how the original program allocates memory for the array. In the class where FLines is defined, it initially reserves ten empty elements for the array in the class constructor. FAllocatedNumLines := 10; GetMem( FLines, FAllocatedNumLines * sizeof( TLayoutLine ) ); And then later if it needs more space for elements, it redefines the array elements as follows: Procedure TRichTextLayout.AddLineStart( Const Line: TLayoutLine ); var NewAllocation: longint; begin if FNumLines >= FAllocatedNumLines then begin // reallocate the array twice the size NewAllocation := FAllocatedNumLines * 2; FLines := ReAllocMem(FLines, NewAllocation * sizeof(TLayoutLine)); FAllocatedNumLines := NewAllocation; end; FLines^[ FNumLines ] := Line; // ***** (1) inc( FNumLines ); end; Like I said, maybe this is related to different implementations of object pascal. By doesn't one allocate memory (array elements) via the SetLength() procedure?

SetLength is a Borland extension. The code as shown is standard pascal code. It's relative complexity simply shows why dyn. arrays were invented.

Andrew Brunner
to Florian Klaempfl

In general I use pointers to data structures and especially when they are arrays.

Take the following

PIntArray: ^TIntArray;
TIntArray:Array of Integer;

1st benefit:

declaring methods associated with classes before TIntArray needs to be defined or declared.

  eg. procedure DoSomething(Var Data:TIntArray); vs (DataP:PIntArray);

Non-obvious benefit to all FPC users

FPC forces the ^ operator while accessing structures as pointers. Delphi didn't force it and I even suspect that memory leaks can result in NOT using the ^ to denote the reference to the pointer rather than the pointer itself.

Lastly, passing by reference rather than by value is much faster. Since copies need not be made when entering a method when entered using a Pointer declaration vs DoSomething(Data:TIntArray). In the latter a copy of the calling Data array would be made, slowing the application and using more memory - and for recursive methods lead to the Stack running out of memory.

Graeme Geldenhuys
to Andrew Brunner
Andrew Brunner wrote
FPC forces the ^ operator while accessing structures as pointers. Delphi didn't force it and I even suspect that memory leaks can result in NOT using the ^ to denote the reference to the pointer rather than the pointer itself.

This was just discussed in another thread. This is not always forced by FPC. eg:

var
  S:  TMyArray;
  pS: ^TMyArray;

then use them as follows:

S[2]
pS[2]

Both work just fine without dereferencing the second line. Weird behaviour, but true.

Lastly, passing by reference rather than by value is much faster. Since copies need not be made when entering a method when entered

OK, so this is only a benefit then if you use Arrays. If I had to use a simple data class (class with no methods), then I can simply pass it as normal or via 'var' parameters and get the same result as you mentioned.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/
Vinzent Höfler
to Andrew Brunner: In general I use pointers to data structures and especially when they are arrays. Take…

Andrew Brunner :

Show quoted text 5 lines
1st benefit: declaring methods associated with classes before TIntArray needs to be defined or declared. eg. procedure DoSomething(Var Data:TIntArray); vs (DataP:PIntArray);

Huh? Is there any difference other than the second one can take a NIL value?

Lastly, passing by reference rather than by value is much faster.

That's what "const" has been invented for: Letting the compiler decide the most efficient way.

Not to mention, expressing your intent:

You don't change it, say so: "const".
You change it, say so: "var".
You initialize it, say so: "out".

If you just take the pointer no-one knows if the value is in/in out/or out. That's clearly not a "benefit".

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01
Andrew Brunner
to Vinzent Höfler
"Vinzent Höfler" wrote 9 lines
Andrew Brunner : > 1st benefit: > > declaring methods associated with classes before TIntArray needs to be > defined or declared. >   eg. procedure DoSomething(Var Data:TIntArray); vs (DataP:PIntArray); Huh? Is there any difference other than the second one can take a NIL value?

Read up on forward declarations. The technique of declaring a typed pointer to any data structure in FPC and Delphi was that you can use it in fields and methods of objects and data structures w/o having to actually have it fleshed out until later in the interface envelope of the unit. I've found that using forward declarations is a saving grace with complicated systems.

> Lastly, passing by reference rather than by value is much faster. That's what "const" has been invented for: Letting the compiler decide the most efficient way.

Oh I don't know about that one. I'd like someone else to comment on that. Does using "const" prohibit copies of the memory. I had memory leaks with Delphi 5 or 6 doing this way back when and I attributed it to trying to pass by reference using const... IMO it should do this but may have had lead me to believe this is not the case... But the fact it that passing by reference is faster than passing by value... That was my only point - which you made quite well. As far as the invention of const descriptor - I only use that for conventional objects since dealing with pointers to data by const or by var is trivial not organization or for maintanince or code review ;-)

Show quoted text 8 lines
Not to mention, expressing your intent: You don't change it, say so: "const". You change it,       say so: "var". You initialize it,   say so: "out". If you just take the pointer no-one knows if the value is in/in out/or out. That's clearly not a "benefit".

Ahhh... The beauty of Pascal over C++ ;-) Buy you're getting way to particular man... You know I've never had a need for Out save for once. LOL.

Marc Weustink
to Graeme Geldenhuys: This was just discussed in another thread. This is not always forced by FPC. eg: var S: …
Graeme Geldenhuys wrote 20 lines
On 11/10/2009, Andrew Brunner wrote: > FPC forces the ^ operator while accessing structures as pointers. > Delphi didn't force it and I even suspect that memory leaks can result > in NOT using the ^ to denote the reference to the pointer rather than > the pointer itself. This was just discussed in another thread. This is not always forced by FPC. eg: var S: TMyArray; pS: ^TMyArray; then use them as follows: S[2] pS[2] Both work just fine without dereferencing the second line. Weird behaviour, but true.

However they have a different meaning.

assume TMyArray=array[1..10] of Byte;

then S[2] refers to the second element of S, being the second byte, while pS[2] refers to the 3 element of pS, being the 3rd TMyArray

Marc

Andrew Brunner
to Marc Weustink
Marc Weustink wrote 9 lines
Graeme Geldenhuys wrote: > > On 11/10/2009, Andrew Brunner wrote: >> >>  FPC forces the ^ operator while accessing structures as pointers. >>  Delphi didn't force it and I even suspect that memory leaks can result >>  in NOT using the ^ to denote the reference to the pointer rather than >>  the pointer itself. >  pS[2] <<<<<<<<<<<<<<<

Any idea what the compiler option is under Lazarus (that's what I use exclusively)?

I get a compiler error when I don't use the ^ operator.

Henry Vermaak
to Andrew Brunner

2009/10/12 Andrew Brunner :

Show quoted text 6 lines
>>  pS[2] <<<<<<<<<<<<<<< Any idea what the compiler option is under Lazarus (that's what I use exclusively)? I get a compiler error when I don't use the ^ operator.

It only works with {$mode delphi} and lazarus uses {$mode objfpc}, i think.

Vinzent Höfler
to Andrew Brunner: On Sun, Oct 11, 2009 at 5:25 PM, "Vinzent Höfler" Read up on forward declarations. The…

Andrew Brunner :

Show quoted text 8 lines
On Sun, Oct 11, 2009 at 5:25 PM, "Vinzent Höfler" Read up on forward declarations. The technique of declaring a typed pointer to any data structure in FPC and Delphi was that you can use it in fields and methods of objects and data structures w/o having to actually have it fleshed out until later in the interface envelope of the unit. I've found that using forward declarations is a saving grace with complicated systems.

Oh, you're talking about what I call "incomplete type"? ;)

I always was under the impression that the pointed-to-type being declared must be completed in the same declaration section, has that changed? If not, I fail to see the benefit, because you still have to complete the type in the same scope. Which is technically the same as directly declaring it in the first place (unless it's self-referencing, of course).

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01
Tomas Hajny
to Graeme Geldenhuys: The other "arrays" discussion got me thinking, and after working so long with only…

On 11 Oct 09, at 10:38, Graeme Geldenhuys wrote:

Hi,

.
.

Show quoted text 37 lines
Anyway to get to my questions, and using the code snippets shown below. This code is all based on the code I am porting from SpeedPascal. So maybe some strange usage is simply due to the age of SpeedPascal etc.. ---------------------- Type TLayoutLine = record Text: PChar; Length: longint; Height: longint; Width: longint; MaxDescender: longint; MaxTextHeight: longint; LinkIndex: longint; Style: TTextDrawStyle; Wrapped: boolean; end; TLinesArray = array[ 0..0 ] of TLayoutLine; -------------------------- 1) Why would you use a pointer to an array and not simply a variable what IS the array? In the program I am porting the following field variable is defined. FLines: ^TLinesArray; Why couldn't I simply say: FLines: TLinesArray; Is there some advantage for using a pointer to the array, when passing it around between procedures and functions?

Yes, there's at least one - you cannot pass (or receive) nil if you use the array directly. Sometimes the API (e.g. OS API) needs the possibility to pass or receive nil.

Show quoted text 5 lines
2) The TLinesArray is a dynamic array, so shouldn't I maybe change the ported code to define the array as follows, instead of the [0..0] idea? TLinesArray = array of TLayoutLine;

Well, it is not a dynamic array strictly speaking, because dynamic arrays maintain number of elements as part of their definition, whereas in this case number of elements is kept elsewhere.

Show quoted text 40 lines
3) Maybe the strange usage (to me at least) of dynamic arrays could be explain in how the original program allocates memory for the array. In the class where FLines is defined, it initially reserves ten empty elements for the array in the class constructor. FAllocatedNumLines := 10; GetMem( FLines, FAllocatedNumLines * sizeof( TLayoutLine ) ); And then later if it needs more space for elements, it redefines the array elements as follows: Procedure TRichTextLayout.AddLineStart( Const Line: TLayoutLine ); var NewAllocation: longint; begin if FNumLines >= FAllocatedNumLines then begin // reallocate the array twice the size NewAllocation := FAllocatedNumLines * 2; FLines := ReAllocMem(FLines, NewAllocation * sizeof(TLayoutLine)); FAllocatedNumLines := NewAllocation; end; FLines^[ FNumLines ] := Line; // ***** (1) inc( FNumLines ); end; Like I said, maybe this is related to different implementations of object pascal. By doesn't one allocate memory (array elements) via the SetLength() procedure? eg: FAllocatedNumLines := 10; SetLength( FLines^, FAllocatedNumLines); I marked on of the lines above with (1). I sometimes get "out of range" errors there, so clearly somewhere in all this dereferencing of arrays etc, I probably made a mistake somewhere in porting/translating the SpeedPascal code to Free Pascal.

Not really, you just use range checking whereas the original code doesn't. The type definition specifies an array with exactly one element [0..0], but you access more elements using that type definition. Technically, there are several ways for such type definitions, but all of them imply that range checking cannot be used any longer. The first one is this one. The second would be defining the upper bound as some very high value (at best as the highest possible, but even that varies e.g. between 16-bit, 32-bit and 64-bit CPUs, so it's not portable easily, and it doesn't give you any range checking either, because although type allows many elements, the real content has fewer elements and the compiler doesn't know how many). The third option (using the pointer mathematics and not available in older Pascal compilers) is declaring the pointer as a pointer to a single element of that array and accessing it as P[x] (without dereferencing this pointer; I'd personally tend to use P[x]^, but that doesn't work, i.e. in this case the pointer isn't a pointer any longer ;-) ).

Tomas