Lazarus

Free Pascal => General => Topic started by: jamie on December 02, 2021, 05:36:49 pm

Title: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 02, 2021, 05:36:49 pm
I use code in a couple of projects but I am using 3.0.4 for various reasons.

I ported over a project to the trunk install I have that still uses 3.2.0 and unless I have issues else where I am not so sure its working as it does with 3.0.4 or I have other issues related to trunk I am not seeing atm.
Below is a simple Laz project but can be done in console mode for those that like that better.
I also attached the project.

I just need to know if the values $FFFFFFFF, 1 show on output with higher than 3.0.4 fpc and if you see any issues there.

This all works fine with delphi, it's an old project.
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$modeSwitch advancedRecords}
  5.  
  6. interface
  7.  
  8. uses
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12.   TMyEmptyIntArray= Packed Record
  13.     Function fGetInteger(Index:Integer):Integer; inline;
  14.     procedure fSetInteger(Index:Integer;Avalue:Integer); Inline;
  15.     property Items[index:integer]:Integer read fgetInteger write fsetInteger; default;
  16.   end;
  17.   TMyIntBaseRecord = Packed Record
  18.     IntCount:DWORD;
  19.     Ints:TMyEmptyIntArray; {an Empty field at the end}
  20.   end;
  21.    PMyIntBaseRecord = ^TMyIntBaseRecord;
  22.   { TForm1 }
  23.  
  24.   TForm1 = class(TForm)
  25.     Button1: TButton;
  26.     procedure Button1Click(Sender: TObject);
  27.   private
  28.  
  29.   public
  30.  
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39. Function TmyEmptyIntArray.fGetInteger(Index:Integer):Integer; inline;
  40. begin
  41.   Result :=PInteger(@Self)[Index];
  42. end;
  43. Procedure TMyEmptyIntArray.fSetInteger(Index:Integer;AValue:Integer);Inline;
  44. Begin
  45.  Pinteger(@Self)[Index]:=aValue;
  46. end;
  47.  
  48. { TForm1 }
  49.  
  50. procedure TForm1.Button1Click(Sender: TObject);
  51. Var
  52.   T:PMyIntBaseRecord;
  53. begin
  54.   T := GetMem(SizeOf(T)+SizeOf(integer)); {Create an item plus one integer at end}
  55.   T^.IntCount := $FFFFFFFF;
  56.   T^.Ints[0]:= 1;
  57.   Caption := T^.IntCount.ToHexString+','+T^.ints[0].Tostring;
  58.   FreeMem(T);
  59. end;
  60.  
  61. end.
  62.  
  63.  

This is just a test app but it represents in small part of a much larger app.

Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: MarkMLl on December 02, 2021, 05:42:38 pm
Debian Linux "Buster" 64-bit Lazarus 2.0.12 FPC 3.2.0 shows FFFFFFFF,1 on the title bar after the button is pressed.

HTH, MarkMLl
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: dseligo on December 03, 2021, 12:21:47 am
FFFFFFFF,1 on Lazarus 2.0.10, FPC 3.2.0, Windows 11
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: trev on December 03, 2021, 12:40:21 am
FFFFFFFF,1 on Lazarus 2.3.0 (git main), FPC 3.3.1 (git main), macOS 12.0.1 (aarch64).
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: marcov on December 03, 2021, 10:06:53 am
It is a quite neat trick if it is legal. (that self of a nested record points to the nested record, not the encompassing one)
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: Zvoni on December 03, 2021, 10:14:11 am
FFFFFFFF,1 on Laz 2.0.12/FPC3.2.0 32 Bit on Win10 Pro 64 Bit
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: howardpc on December 03, 2021, 12:29:46 pm
It is a quite neat trick if it is legal. (that self of a nested record points to the nested record, not the encompassing one)
Although how could a nested Self point to anything else? It has no knowledge of being nested, it knows only itself.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 03, 2021, 12:40:29 pm
It is a quite neat trick if it is legal. (that self of a nested record points to the nested record, not the encompassing one)

 I would hope it would be legal, I've doing such tricks for some time now and the SELF. It would be broken if not pointing to the immediate record when referenced which btw is also part of the parent record that over hangs at the end. That is the intent here.

 This is to support Structs (Records) with empty count arrays at the end of the struct(record).


 The test app I posted here is just a model of a much larger app with a lot more content in the records of course and I did last night move it to the PC with the Trunk install and that seems to work but the app as a whole appears to be getting bad data when using 3.2.x and I need to determine where this is taking place so I can move on.

BTW, I discovered that the OBJECT model still supports this and can be used that way too instead of an advanced record, meaning the tail record can be an object instead.
 The old original code was this way to support the over hang at the end.

 Thank you much for testing, all of you.

Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: Warfley on December 03, 2021, 01:34:51 pm
In C code you can often see people use a similar trick:
Code: C  [Select][+][-]
  1. struct {
  2.   ...
  3.   int items[0];
  4. }
This also works in Pascal:
Code: Pascal  [Select][+][-]
  1.   TTestRec = record
  2.     A: Integer;
  3.     Items: Array[0..0] of Integer;
  4.   end;
But throws warnings


Note: you should also not forget, if using that, to always call Initialize and Finalize on the memory for managed types
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: marcov on December 03, 2021, 01:39:49 pm
Warfley: the pascal definition has one element and thus enlarges the record
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: Warfley on December 03, 2021, 01:50:26 pm
You are right, there is no way in Pascal to make a 0 sized array.

I've found another solution that does not throw warnings and requires less code:
Code: Pascal  [Select][+][-]
  1. type
  2.   PTestRec = ^TTestRec;
  3.   TTestRec = packed record
  4.     __startofitems: packed record end;
  5.     function Items: PInteger; inline;
  6.   end;
  7.  
  8. function TTestRec.Items: PInteger;
  9. begin
  10.   Result := @__startofitems;
  11. end;
  12.  
  13. var
  14.   p: PTestRec;
  15. begin
  16.   WriteLn(SizeOf(TTestRec));
  17.   p := GetMem(SizeOf(TTestRec) + 4 * SizeOf(Integer));
  18.   p^.Items[2] := 42;
  19.   WriteLn(p^.Items[2]);
But compiling this let's the FPC enter an infinite loop with 100% CPU usage so. This is probably a bug in the FPC

Edit: I am kinda an idiot, there is absolutely no need for that dummy field (which is kinda funny because I used this exact mechanism already multiple times in my code, and never used a dummy field before, don't know why I wanted to use it now):
Code: Pascal  [Select][+][-]
  1. type
  2.   PTestRec = ^TTestRec;
  3.   TTestRec = packed record
  4.     function Items: PInteger; inline;
  5.   end;
  6.  
  7. function TTestRec.Items: PInteger;
  8. begin
  9.   Result := PInteger(Pointer(@self) + SizeOf(Self));
  10. end;
  11.  
  12. var
  13.   p: PTestRec;
  14. begin
  15.   WriteLn(SizeOf(TTestRec));
  16.   p := GetMem(SizeOf(TTestRec) + 4 * SizeOf(Integer));
  17.   p^.Items[2] := 42;
  18.   WriteLn(p^.Items[2]);
  19. end.
This now also makes the FPC compile it.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 03, 2021, 02:53:16 pm
Interesting you found a compiler loop bug, maybe that should be reported ?

In any case, if you were to have a record with the last valid field being the same type as the empty array you can pull this trick which also works.

Code: Pascal  [Select][+][-]
  1. TTheRecord = Record
  2.  Case Byte of
  3.    Count:(CountInt:Integer);
  4.    Ints:Array[-1..-1] of Integer;
  5. End;  
  6.  
;
 Just  index it starting at 0

BUt this  of course only works if the  prior data type is the same

Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: marcov on December 03, 2021, 03:06:42 pm
I have some doubts about the sizeof(self)  (always pointer size =4/8 or nested record size=0?)
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: Warfley on December 03, 2021, 03:10:39 pm
Quote
In any case, if you were to have a record with the last valid field being the same type as the empty array you can pull this trick which also works.
This only works for non managed types. Managed types like strings can not occur in the variant parts of a record. But yes, in this case, it would be possible without requireing a function.

I have some doubts about the sizeof(self)  (always pointer size =4/8 or nested record size=0?)
This is actually quite a good question. The pointer size does not really matter but the record size does. I think that with packed it should always be 0 but I am not sure if this is the case and/or is documented somewhere.

One exception I could think of is, when more advanced RTTI comes, that records might get hidden RTTI fields added if enabled. But TBH I don't know how the plans are for RTTI in advanced records
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 03, 2021, 03:29:50 pm
Quote
This only works for non managed types. Managed types like strings can not occur in the variant parts of a record. But yes, in this case, it would be possible without requireing a function.


Well I don't use managed types in this type of code since it's reading external data to start with.

I do have array of char in the records and I use Short strings at times.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: PascalDragon on December 03, 2021, 04:16:54 pm
This all works fine with delphi, it's an old project.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. Var
  3.   T:PMyIntBaseRecord;
  4. begin
  5.   T := GetMem(SizeOf(T)+SizeOf(integer)); {Create an item plus one integer at end}
  6.   T^.IntCount := $FFFFFFFF;
  7.   T^.Ints[0]:= 1;
  8.   Caption := T^.IntCount.ToHexString+','+T^.ints[0].Tostring;
  9.   FreeMem(T);
  10. end;
  11.  

I assume you mean SizeOf(TMyIntBaseRecord) + SizeOf(Integer) instead of SizeOf(P) as otherwise you might get a nasty surprise on 32-bit systems ;)

It is a quite neat trick if it is legal. (that self of a nested record points to the nested record, not the encompassing one)

 I would hope it would be legal, I've doing such tricks for some time now and the SELF. It would be broken if not pointing to the immediate record when referenced which btw is also part of the parent record that over hangs at the end. That is the intent here.

It is legal. And rather elegant I have to say compared to some other “hacks”.

I've found another solution that does not throw warnings and requires less code:

If one writes the record once as a generic one can easily reuse it (and maybe we should even add this to the RTL... :-X ):

Code: Pascal  [Select][+][-]
  1. program trecarr;
  2.  
  3. {$mode objfpc}
  4. {$modeswitch advancedrecords}
  5.  
  6. uses
  7.   SysUtils;
  8.  
  9. type
  10.   generic TEndOfRecArray<T> = packed record
  11.   private type
  12.     PT = ^T;
  13.   private
  14.     function GetItem(aIndex: Integer): T; inline;
  15.     procedure SetItem(aIndex: Integer; aValue: T); inline;
  16.   public
  17.     property Items[Index: Integer]: T read GetItem write SetItem; default;
  18.   end;
  19.  
  20. function TEndOfRecArray.GetItem(aIndex: Integer): T;
  21. begin
  22.   Result := PT(@Self)[aIndex];
  23. end;
  24.  
  25. procedure TEndOfRecArray.SetItem(aIndex: Integer; aValue: T);
  26. begin
  27.   PT(@Self)[aIndex] := aValue;
  28. end;
  29.  
  30. type
  31.   TMyIntBaseRecord = packed record
  32.     IntCount: LongWord;
  33.     Ints: specialize TEndOfRecArray<LongInt>;
  34.   end;
  35.   PMyIntBaseRecord = ^TMyIntBaseRecord;
  36.  
  37. var
  38.   p: PMyIntBaseRecord;
  39. begin
  40.   p := GetMem(SizeOf(TMyIntBaseRecord) + SizeOf(LongInt));
  41.   p^.IntCount := $FFFFFFFF;
  42.   p^.Ints[0] := 1;
  43.   Writeln(p^.IntCount.ToHexString, ', ', p^.Ints[0].ToString);
  44.   FreeMem(p);
  45. end.

But compiling this let's the FPC enter an infinite loop with 100% CPU usage so. This is probably a bug in the FPC

Would you please report that? :)

I have some doubts about the sizeof(self)  (always pointer size =4/8 or nested record size=0?)
This is actually quite a good question. The pointer size does not really matter but the record size does. I think that with packed it should always be 0 but I am not sure if this is the case and/or is documented somewhere.

It should be 0, cause when using SizeOf from the outside returns the same, but for some reason it's 1... %)

One exception I could think of is, when more advanced RTTI comes, that records might get hidden RTTI fields added if enabled. But TBH I don't know how the plans are for RTTI in advanced records

The RTTI is per type, not per instance of that type, thus there is nothing related to RTTI stored in the record itself.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 03, 2021, 04:57:27 pm
Quote
I assume you mean SizeOf(TMyIntBaseRecord) + SizeOf(Integer) instead of SizeOf(P) as otherwise you might get a nasty surprise on 32-bit systems ;)


Yes that is what I meant, That was a slipup in the Test demo because I started from a non pointer type reccord but you see what the intent is.

Good catch.   :)
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 03, 2021, 05:40:41 pm
You guys with your GENERICS !  >:D
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: SymbolicFrank on December 03, 2021, 05:57:24 pm
I don't get it. Isn't that how lists and such work? At least in Delphi, they were declared like this:

Code: Pascal  [Select][+][-]
  1. type
  2.   TSomeList: array[0..MaxInt - 1] of SomeType;    

Or just a pointer to the first element. And code to manage the size.

As far as I get it, you want to do the same, but manage it yourself? Or is this to have the memory footprint of a C-struct:

Code: C  [Select][+][-]
  1. struct SomeArray {
  2.     int Length;
  3.     int* Elements;
  4. };

Like Pascal strings/arrays?
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 03, 2021, 06:29:31 pm
That would generate a large record field that isn't empty..

The idea is to have a define without substance so the record can be used as a header to a chuck of data that follows without actually spanning into that region without memory.

 Your example would cause some issues even if you were use a Getmem call and subtracting that field size.

 This is to reproduce what other languages do with having zero sized arrays at the end of the struct or at any place for that matter.

  this generates a place holder of a memory address and in this case a hang over.

 Using your method you would need to subract that field when dynamically creating the record and if using it as a static record or one on the heap will just flood the pool.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: Warfley on December 03, 2021, 06:35:43 pm
The difference between this and a pointer to a memory location is that the list is directly tied to the control structure in the same memory region.
For example, in the DNS protocoll, you have a header followed by the DNS records in a memory list. This could be directly implemented with such a datastructure, without requiring any additional control data and code, which can be extremly helpful if you need to process such information at a high performance (e.g. when implementing your own DNS server)

What can be done alternatively is the following:
Code: Pascal  [Select][+][-]
  1. PMyRecord = ^TMyRecord;
  2. TMyRecord = record
  3.   // Header...
  4.   Items: Array[0..SizeInt.MaxValue] of Integer;
  5. end;
  6.  
  7. p: PMyRecord;
  8. begin
  9.   p := GetMem(HeaderSize + Itemsize);
  10.   ...
  11. end;
But this is kinda inconvinient, as you need to compute the Headersize by yourself, while with the datastructures shown here, you can simply use sizeof and let the compiler do the work. Especially if the header might be changed in the future, sizeof will always return the true size, while in the other case you need to update every instance where you compute the size
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: SymbolicFrank on December 03, 2021, 06:46:44 pm
Yes, that's what you put inside a class, to hold the actual data.

Code: Pascal  [Select][+][-]
  1. type
  2.   TItem = Integer;
  3.   PItems = ^TItems;
  4.   TItems = Array[0..MaxInt - 1] of TItem;
  5.  
  6.   { TItemList }
  7.  
  8.   TItemList = class
  9.   private
  10.     FItems: TItems;
  11.     ItemCount: Integer;
  12.     ItemsSize: Integer;
  13.     function GetItem(Index: Integer): TItem;
  14.     procedure SetItem(Index: Integer; AValue: TItem);
  15.     procedure SetItems(AValue: TItems);
  16.   public
  17.     constructor Create;
  18.     destructor Destroy;
  19.     function Add(TItem): Integer;
  20.     property Count: Integer read ItemCount;
  21.     property Items: TItems read FItems write SetItems;
  22.     property Item[Index: Integer]: TItem read GetItem write SetItem; default;
  23.   end;

Better example (x3).
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: SymbolicFrank on December 03, 2021, 07:22:53 pm
Ok, now the example is better. The property Items is only included because it can be done, not that you should do that (it defeats the purpose of the class).
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: Warfley on December 03, 2021, 08:22:46 pm
It should be noted that this can run into problems with Initialize.

So whenever raw memory is allocated through GetMem (or malloc if the c memory manager is used), before using it it must be initialized. The reason for this is to perform all the neccessary management operations on managed types, which since the introduction of management operators, could be any record now.

Consider the following (using string as an example here as managed type):
Code: Pascal  [Select][+][-]
  1. type
  2.   PTestRec = ^TTestRec;
  3.   TTestRec = record
  4.     Name: String;
  5.     Items: array[0..MaxInt] of String;
  6.   end;
  7.  
  8. var
  9.   p: PTestRec;
  10. begin
  11.   p := GetMem(1024);
  12.   Initialize(p^);
  13. end.
The initialize will look at the type TTestRec and see it is a normal record, so it will initialize every single element of that record. When considering Items it sees MaxInt many Strings, so it tries to initialize them. But the allocated memory only covers 1024/sizeof(String) strings, meaning it will cause the initialize to overflow on the allocated memory, and in this example most likely cause a segfault.
But initialize is defenetly needed, as otherwise both the name and the items are not usable

When considering a type with a function or [] property, it can be done like this very easiely and safe:
Code: Pascal  [Select][+][-]
  1. type
  2.   PTestRec = ^TTestRec;
  3.  
  4.   TTestRec = record
  5.     Name: String;
  6.     function Items: PString;
  7.     procedure Destroy;
  8.  
  9.     class function Alloc(Count: SizeInt): PTestRec; static;
  10.   end;
  11.  
  12. function TTestRec.Items: PString;
  13. begin
  14.   Result := PString(Pointer(@self) + SizeOf(Self));
  15. end;
  16.  
  17. procedure TTestRec.Destroy(Count: SizeInt);
  18. var
  19.   i: SizeInt;
  20. begin
  21.   for i:=0 to Count-1 do
  22.     Finalize(Items[i]);
  23.   Finalize(Self);
  24. end;
  25.  
  26. class function TTestRec.Alloc(Count: SizeInt): PTestRec;
  27. var
  28.   i: SizeInt;
  29. begin
  30.   Result := GetMem(SizeOf(TTestRec) + Count * SizeOf(String));
  31.   Initialize(Result^);
  32.   for i:=0 to Count-1 do
  33.     Initialize(Result^.Items[i]);
  34. end;
  35.  
  36. var
  37.   p: PTestRec;
  38. begin
  39.   p := TTestRec.Alloc(1024);
  40.   try
  41.  
  42.   finally
  43.     p^.Destroy(1024);
  44.     Freemem(p);
  45.   end;
  46. end.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: SymbolicFrank on December 03, 2021, 09:15:31 pm
Well, yes. But that's why you normally double the size of the array when it gets full and simply copy everything to the new one. And why you're not using pointers, but the array. You're not going to initialize memory for each element. If you want that, use a linked list instead.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: marcov on December 03, 2021, 09:36:52 pm
If one writes the record once as a generic one can easily reuse it (and maybe we should even add this to the RTL... :-X ):

That was what I thinking too, not in the RTL, but in my own root record. (Records because they are opengl vertex buffers)
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: SymbolicFrank on December 04, 2021, 01:11:53 am
If one writes the record once as a generic one can easily reuse it (and maybe we should even add this to the RTL... :-X ):

That was what I thinking too, not in the RTL, but in my own root record. (Records because they are opengl vertex buffers)
Why? Could you explain?
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 04, 2021, 01:35:50 am
Playing with the Generic's sample that Pascal posted it compiles etc but it does not INLINE.

do it without generics and it does inline.

I did this on 3.0.4 fpc so I don't know if higher levels have fixed this ?

For now I will stick with non generics because I don't want the calls for the setter and getter...
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: jamie on December 04, 2021, 02:44:52 am
It seems that if I am using a GUI app and do that code within a Button Click event which is a procedure of OBJECT the generics version does not inline but not using GUI or maybe even not doing Method of Classes/Record calls embedded does work either way.

 Something about the compiler not happy with inlining when type is declared within an Object body.

 Maybe the compiler is not dealing with the background code that implies the SELF pointer etc.

 its strange this would work outside of method bodies though?


 Anyways, I know the non-generic version works both ways and will stick with it.

 Thanks.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: Warfley on December 04, 2021, 03:51:32 am
You're not going to initialize memory for each element. If you want that, use a linked list instead.
Initialize is always called on each element, just normally it is hidden by compiler magic. TList from Generics.Collections for example uses "array of T" and allocates the memory with Setlength, which will internally call Initialize for each element.

You *always* need to initialize the memory for each element. The question is just if it is done automatically, either through a constructor, New or SetLength, or if you need to do it manually which you need to do if you use raw allocation e.g. via getmem, stack allocation or, in case of microcontrollers and you don't have virtual memory, when performing direct memory access.

This can also be easiely tested check the memory consumption of the following two programs:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. type
  6.   TLargeArray = Array[1..1024*1024*1024 div SizeOf(String)] of String; // 1 GB memory
  7.   PLargeArray = ^TLargeArray;
  8. var
  9.   p: PLargeArray;
  10. begin
  11.   p := GetMem(SizeOf(TLargeArray));
  12.   ReadLn;
  13.   FreeMem(p);
  14. end.
  15.  
and
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. type
  6.   TLargeArray = Array[1..1024*1024*1024 div SizeOf(String)] of String; // 1 GB memory
  7.   PLargeArray = ^TLargeArray;
  8. var
  9.   p: PLargeArray;
  10. begin
  11.   New(p);
  12.   ReadLn;
  13.   Dispose(p);
  14. end.
  15.  
The first program takes takes up no physical memory at all (only 0.6 MB when launched with debugger symbols), while the second one takes 1GB of memory.
The reason for that is that allocating virtual memory does by itself not use any physical memory, only once a page is touched it is loaded into actual memory. What happens in the second example that implicetly (as part of calling New), the code will iterate over every single element of the array and call initialize on it.
But the first program is not complete yet, you can't use any array element until you called Initialize on it.

As a simple rule of thumb: If you use GetMem, always call Initialize and if you use FreeMem always use Finalize.

PS: You can use this as an alternative to geometric growth, to allocate large chunks of virtual memory in advance but only touch the elements (and call Initialize on them) when you need them (for performance reasons this can also be done in a geometric fashion). If you know the maximum size of the list beforehand, and also have enough virtual memory available (2^48 bytes on most modern x86_64 CPUs) this can improve performance by avoiding multiple calls to the memory managers and also copy operations.
Title: Re: Zero Sized Array at end of Records, can this test code be verified ?
Post by: PascalDragon on December 06, 2021, 02:06:52 pm
Or is this to have the memory footprint of a C-struct:

Code: C  [Select][+][-]
  1. struct SomeArray {
  2.     int Length;
  3.     int* Elements;
  4. };

Like Pascal strings/arrays?

No, it's to get the equivalent for the following C-struct:

Code: C  [Select][+][-]
  1. struct SomeArray {
  2.   int Length;
  3.   int Elements[];
  4. }

Which is different from the one you posted, because the elements of Elements directly follow SomeArray where as in your example the data of Elements might be anywhere. These kind of embedded arrays are rather common for example in the Windows API.

Playing with the Generic's sample that Pascal posted it compiles etc but it does not INLINE.

do it without generics and it does inline.

I did this on 3.0.4 fpc so I don't know if higher levels have fixed this ?

For now I will stick with non generics because I don't want the calls for the setter and getter...

That was indeed improved with 3.2.0 as that also introduced generic routines and for those I definitely wanted inlining so I had to rework the way this is handled inside the compiler a bit. The important point however is: The body of the generic needs to have been parsed before the call that is supposed to be inlined (which is consistent with non-generic code).
TinyPortal © 2005-2018