Recent

Author Topic: Array is not empty when entering function  (Read 1032 times)

LemonParty

  • Sr. Member
  • ****
  • Posts: 353
Array is not empty when entering function
« on: October 17, 2025, 09:03:10 am »
I have a unit:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode ObjFPC}{$H+}
  4. {$modeSwitch advancedRecords}
  5.  
  6. interface
  7.  
  8. uses
  9.   Classes, SysUtils;
  10.  
  11. type
  12.   TSizeUIntDynArray = array of SizeUInt;
  13.  
  14.   { TRec }
  15.  
  16.   TRec = record
  17.     Fld: Integer;
  18.     function Count: TSizeUIntDynArray;
  19.   end;
  20.  
  21. implementation
  22.  
  23. { TRec }
  24.  
  25. function TRec.Count: TSizeUIntDynArray;
  26. var
  27.   i: Integer;
  28. begin
  29.   Writeln(Length(Result));
  30.   SetLength(Result, Fld);
  31.   for i:= 0 to Pred(Fld) do begin
  32.     Result[i]:= i;
  33.   end;
  34. end;
  35.  
  36. end.
  37.  

Then in program I do:
Code: Pascal  [Select][+][-]
  1. uses Unit1;
  2.  
  3. var
  4.   R: TRec;
  5.   Arr: TSizeUIntDynArray;
  6. begin
  7.   R.Fld:= 5;
  8.   Arr:= R.Count;
  9.   R.Fld:= 10;
  10.   Arr:= R.Count;
  11. end.
  12.  
Output is:
Quote
0
5
Is this bug?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

Zvoni

  • Hero Member
  • *****
  • Posts: 3135
Re: Array is not empty when entering function
« Reply #1 on: October 17, 2025, 09:47:14 am »
You are writeln-ing the Length of the Result BEFORE setting the Length to the Value of fld

Why would you expect something else?

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode ObjFPC}{$H+}
  3. {$modeSwitch advancedRecords}
  4. type
  5.   TSizeUIntDynArray = array of SizeUInt;
  6.   TRec = record
  7.       Fld: Integer;
  8.       function Count: TSizeUIntDynArray;
  9.     end;
  10.  
  11. var
  12.   R: TRec;
  13.   Arr: TSizeUIntDynArray;
  14.  
  15. function TRec.Count: TSizeUIntDynArray;
  16. var
  17.   i: Integer;
  18. begin
  19.   SetLength(Result, Fld);
  20.   for i:= 0 to Pred(Fld) do begin
  21.     Result[i]:= i;
  22.   end;
  23.   Writeln(Length(Result));
  24. end;
  25. begin
  26.   R.Fld:= 5;
  27.   Arr:= R.Count;
  28.   Writeln(Length(arr));
  29.   R.Fld:= 10;
  30.   Arr:= R.Count;
  31.   Writeln(Length(arr));
  32.   Readln;        
  33. end.

Returns
Quote
5
5
10
10
« Last Edit: October 17, 2025, 09:49:25 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Handoko

  • Hero Member
  • *****
  • Posts: 5485
  • My goal: build my own game engine using Lazarus
Re: Array is not empty when entering function
« Reply #2 on: October 17, 2025, 09:54:49 am »
If OP compiles it using Lazarus IDE, it will clear to know where the problem is:

Zvoni

  • Hero Member
  • *****
  • Posts: 3135
Re: Array is not empty when entering function
« Reply #3 on: October 17, 2025, 10:01:09 am »
If OP compiles it using Lazarus IDE, it will clear to know where the problem is:
Or is he complaining about his second output (the "5")?

A dynamic Array is a managed type out of the gates, and in his calling routine he keeps a reference to the dynamic array he received in his first call.
Meaning: "Result" doesn't go out of scope for itself to "reset" for the second call

EDIT: after some tests i'm not sure if i'm even close to the reason

Though i found this:
https://www.freepascal.org/docs-html/ref/refse94.html
Quote
Remark Function results are treated as pass-by-reference parameters. That is especially important for managed types: The function result may be non-nil on entry, and set to a valid instance of the type.
« Last Edit: October 17, 2025, 10:09:04 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

cdbc

  • Hero Member
  • *****
  • Posts: 2462
    • http://www.cdbc.dk
Re: Array is not empty when entering function
« Reply #4 on: October 17, 2025, 10:07:25 am »
Hi
@LemonParty: No it's not a bug! When you enter a function/method, then the stack-memory is full of garbage from the last allocations in that spot.
Which you also can see from your results: on first run after program-start, it returns 0, i.e.: there was no residue from last allocation, on the second run, it returns 5, which was exactly what was in the memory-space before... :-X
Always initialize your variables before use!
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Handoko

  • Hero Member
  • *****
  • Posts: 5485
  • My goal: build my own game engine using Lazarus
Re: Array is not empty when entering function
« Reply #5 on: October 17, 2025, 10:15:07 am »
Always initialize your variables before use!
;D

Thaddy

  • Hero Member
  • *****
  • Posts: 18304
  • Here stood a man who saw the Elbe and jumped it.
Re: Array is not empty when entering function
« Reply #6 on: October 17, 2025, 10:16:19 am »
@Zvoni

Did you miss that the array is not part of the record?
And the function takes no parameters.
Therefor result is undefined without initialization.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Zvoni

  • Hero Member
  • *****
  • Posts: 3135
Re: Array is not empty when entering function
« Reply #7 on: October 17, 2025, 10:41:33 am »
@Zvoni

Did you miss that the array is not part of the record?
And the function takes no parameters.
Therefor result is undefined without initialization.
No, i didn't miss that.
But as the Docs say: Result is Pass-by-reference, and there is a Variable outside the Record (the "arr"-Var) HOLDING that reference!
Remember: Dyn. Array is a managed Type with a RefCount
Also see cdbc's answer: the stack-memory is full of garbage from the last allocations in that spot.

By any chance: Do you know how to retrieve the RefCount for "Result"?
I don't. because that would actually clear up if i'm right or wrong (would welcome any answer, since i could learn something)

EDIT: or better said: I know that a dyn. Array has a Descriptor at negative offset, but i have no clue how to access that
« Last Edit: October 17, 2025, 11:03:49 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12523
  • FPC developer.
Re: Array is not empty when entering function
« Reply #8 on: October 17, 2025, 10:45:58 am »
Yes. Doing it this way has the potential to save reallocations.

e.g.

Code: Pascal  [Select][+][-]
  1. var r : TSomeDynamicArray;
  2.   for i:=0 to x-1 do
  3.     begin
  4.        r:=Thefunction();
  5.       // do something readonly with R
  6.     end;
  7.  

If r typically has say 3 elements this saves many resizes.

Thaddy

  • Hero Member
  • *****
  • Posts: 18304
  • Here stood a man who saw the Elbe and jumped it.
Re: Array is not empty when entering function
« Reply #9 on: October 17, 2025, 12:05:06 pm »
By any chance: Do you know how to retrieve the RefCount for "Result"?

If you promise not to use it, here's one for managed types ( except strings):
Code: Pascal  [Select][+][-]
  1. f{$ifdef fpc}{$mode delphi}{$endif}{$pointermath on}
  2. uses types,typinfo;
  3. function GetRefCount<T>(const s: T): LongInt;
  4. begin
  5.   Result := 0;
  6.   if IsManagedType(T) tand not (getTypeKind(T) in [tkAString,tkWstring]) then // add more stringtypes.
  7.     if length(s)>0 then // can not leave this out
  8.     result := PPtrInt(PByte(s) - SizeOf(PtrInt) * 2)^;
  9. end;
  10.  
  11. var
  12.   a,b,c:TIntegerDynArray;
  13.  begin
  14.   setlength(a,10);
  15.   b:=a;
  16.   writeln(getrefcount<TIntegerDynArray>(b));
  17.   c:=b;
  18.   writeln(getrefcount<TIntegerDynArray>(b));
  19. end.
This is highly relying on implementation detail.
Code: Text  [Select][+][-]
  1. String (32-bit):
  2. -12: CodePage (Word)
  3. -10: ElementSize (Word)
  4. -8: RefCount (LongInt)
  5. -4: Length (LongInt)
  6.  
  7. String (64-bit):
  8. -24: CodePage (Word)
  9. -22: ElementSize (Word)
  10. -16: RefCount (LongInt)
  11. -8: Length (LongInt)
  12.  
  13. Dynamic Array (32-bit):
  14. -12: RefCount (LongInt)
  15. -8: High (LongInt) -> length = High+1
  16. -4: TypeInfo (Pointer)
  17.  
  18. Dynamic Array (64-bit):
  19. -16: RefCount (LongInt)
  20. -12: High (LongInt)
  21. -8: TypeInfo (Pointer)
Taken from the compiler sources.
« Last Edit: October 17, 2025, 12:10:51 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11792
  • Debugger - SynEdit - and more
    • wiki
Re: Array is not empty when entering function
« Reply #10 on: October 17, 2025, 12:13:23 pm »
By any chance: Do you know how to retrieve the RefCount for "Result"?

In the debugger, if using FpDebug

  :refcnt(thearray)

cdbc

  • Hero Member
  • *****
  • Posts: 2462
    • http://www.cdbc.dk
Re: Array is not empty when entering function
« Reply #11 on: October 17, 2025, 12:15:29 pm »
Hi Thaddy
Please explain to me the 4 dummy bytes(16..20) in here:
Quote
String (64-bit):
-24: CodePage (Word)
-22: ElementSize (Word)
-16: RefCount (LongInt)
-8: Length (LongInt)
I get it's register-size-aligned, but why not just 2 cardinals?!?
I guess -- Future Use...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11792
  • Debugger - SynEdit - and more
    • wiki
Re: Array is not empty when entering function
« Reply #12 on: October 17, 2025, 12:22:52 pm »
Yes. Doing it this way has the potential to save reallocations.

True, but it can go the other way.

To save the re-alloc, the code must not contain "result := nil". => Which would be the way to initialize result.

Because
   "SetLenght(result, NewLen);"
gives the "managed type not initialized" hint. (which is needed to omit the re-alloc, if the size is kept or changed within bounds that fit current mem alloc)

But, if you don't set to "nil" first, and if the old value that happens to be in result, is a very large array, and if you increase the size further...
e.g.  you have
   SetLenght(Result, CachedPreviousUsedResLen + large_increase);

Then the old memory may need to be relocated. Leading to a (most likely) unnecessary copy of large memory chunks.

Thaddy

  • Hero Member
  • *****
  • Posts: 18304
  • Here stood a man who saw the Elbe and jumped it.
Re: Array is not empty when entering function
« Reply #13 on: October 17, 2025, 12:28:13 pm »
@cdbc
Code: Pascal  [Select][+][-]
  1. //dynarr.inc
  2.        { include for proper alignment }
  3.        tkInt64: (
  4.          dummy : Int64
  5.        );
  6.  
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

cdbc

  • Hero Member
  • *****
  • Posts: 2462
    • http://www.cdbc.dk
Re: Array is not empty when entering function
« Reply #14 on: October 17, 2025, 12:30:29 pm »
Hi Thaddy
Ahumm... That's 8 bytes on 64bit, we only need to account for 4?!?
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6 -> FPC 3.2.2 -> Lazarus 4.0 up until Jan 2025 from then on it's both above &: KDE6/QT6 -> FPC 3.3.1 -> Lazarus 4.99

 

TinyPortal © 2005-2018