Recent

Author Topic: Array Question  (Read 2434 times)

nugax

  • Full Member
  • ***
  • Posts: 232
Array Question
« on: May 05, 2022, 03:11:41 am »
Lets say i have an array of records.

How can I clear the array of the elements, so that I can ensure that it is empty the next time I load it?

E.g.

type
  s = array of recordtype;

Can I use setlength(s, 0)?

Or what would I use to clear the array?
-Nugax

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Array Question
« Reply #1 on: May 05, 2022, 03:48:23 am »
Use SetLength(DynamicArrayVar, 0); the array will be cleared and removed from memory. Dynamic arrays are finalized automatically, it is not necessary to manually SetLength(…, 0).

Read more:
https://www.freepascal.org/docs-html/rtl/system/setlength.html
https://wiki.freepascal.org/Dynamic_array#handling

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Array Question
« Reply #2 on: May 05, 2022, 08:55:35 am »
SetLength(arr, 0) is fine, but you can also just assign it to nil, this is shorter:
Code: Pascal  [Select][+][-]
  1. arr := nil;

Zvoni

  • Hero Member
  • *****
  • Posts: 2319
Re: Array Question
« Reply #3 on: May 05, 2022, 10:11:00 am »
SetLength(arr, 0) is fine, but you can also just assign it to nil, this is shorter:
Code: Pascal  [Select][+][-]
  1. arr := nil;
It might be shorter, but it's IMO counter-intuitive.
arr:=nil
implies setting a Pointer to nil, while SetLength(DynArr,0) clearly points out, that i'm setting a (dynamic) Array to Length of "0" (a.k.a clearing it).

But that's just personal preference
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

jcmontherock

  • Full Member
  • ***
  • Posts: 234
Re: Array Question
« Reply #4 on: May 05, 2022, 11:02:13 am »
What about the records ? Are they erased  when we write "SetLength(array of records, 0)"  ?
Windows 11 UTF8-64 - Lazarus 3.2-64 - FPC 3.2.2

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Array Question
« Reply #5 on: May 05, 2022, 11:14:54 am »
It might be shorter, but it's IMO counter-intuitive.
arr:=nil
implies setting a Pointer to nil, while SetLength(DynArr,0) clearly points out, that i'm setting a (dynamic) Array to Length of "0" (a.k.a clearing it).

But that's just personal preference
I think this is kinda funny, because the term "nil" originally came from LISP where it describes the empty list, and was later repurposed by other languages to mean the null pointer. So in some way setting an array to nil is more fitting than using it for pointers :D

What about the records ? Are they erased  when we write "SetLength(array of records, 0)"  ?
yes, both setlength and setting to nil will finalise the records and release the memory

jcmontherock

  • Full Member
  • ***
  • Posts: 234
Re: Array Question
« Reply #6 on: May 05, 2022, 11:26:59 am »
It's a good news: arrays "OwnsObjects" by default...  :D
Windows 11 UTF8-64 - Lazarus 3.2-64 - FPC 3.2.2

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Array Question
« Reply #7 on: May 05, 2022, 12:29:11 pm »
It might be shorter, but it's IMO counter-intuitive.
arr:=nil
implies setting a Pointer to nil, while SetLength(DynArr,0) clearly points out, that i'm setting a (dynamic) Array to Length of "0" (a.k.a clearing it).

But that's just personal preference

Also it is redolent of the unpleasant odour of C's equivalence between pointers and arrays.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Array Question
« Reply #8 on: May 05, 2022, 12:36:40 pm »
It's a good news: arrays "OwnsObjects" by default...  :D

Careful with your terminology there: an array doesn't "own" anything.

"Own" has a very specific meaning in the context of some of the FPC and Lazarus libraries, where it implies that a component that contains other instances will free/destroy them recursively.

An array containing (pointers to) objects or instances of classes will not free/destroy that content.

An array (or record etc.) containing dynamically-sized entities (dynamic arrays, strings) will release them as necessary.

MarkMLl




MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

wp

  • Hero Member
  • *****
  • Posts: 11855
Re: Array Question
« Reply #9 on: May 05, 2022, 12:46:39 pm »
What about the records ? Are they erased  when we write "SetLength(array of records, 0)"  ?
A normal static record, yes. But when you dynamically allocate memory for the record on the heap by yourself (New(), GetMem()), you are responsible to free it. The following example creates a memory leak with the commented line. Uncomment it to remove the memory leak.
Code: Pascal  [Select][+][-]
  1. type
  2.   TDataRec = record
  3.     x: Integer;
  4.     y: Integer;
  5.     s: string;
  6.   end;
  7.   PDataRec = ^TDataRec;
  8. var
  9.   dataArray: array of PDataRec = nil;
  10.   i: Integer;
  11. begin
  12.   Setlength(dataArray, 3);
  13.  
  14.   New(dataArray[0]);
  15.   dataArray[0]^.x := 0;
  16.   dataArray[0]^.Y := 0;
  17.   dataArray[0]^.s :=  'a';
  18.  
  19.   New(dataArray[1]);
  20.   dataArray[1]^.x := 1;
  21.   dataArray[1]^.Y := 1;
  22.   dataArray[1]^.s := 'b';
  23.  
  24.   New(dataArray[2]);
  25.   dataArray[2]^.x := 2;
  26.   dataArray[2]^.Y := 2;
  27.   dataArray[2]^.s := 'c';
  28.  
  29.   //for i := 0 to High(dataArray) do Dispose(dataArray[i]);
  30.   SetLength(dataArray, 0);
  31. end.

[EDIT]
MarkMLI was faster...
« Last Edit: May 05, 2022, 12:50:09 pm by wp »

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Array Question
« Reply #10 on: May 05, 2022, 01:36:25 pm »
Finalization means that the "Finalize" intrinsic is called on the data, not that Free is called on objects.

For example:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch advancedrecords}
  5.  
  6. uses
  7. unit1, heaptrc;
  8.  
  9. type
  10.   TTest = record
  11.     Ptr: PInteger;
  12.  
  13.     class operator Finalize(var t: TTest);
  14.   end;
  15.  
  16. class operator TTest.Finalize(var t: TTest);
  17. begin
  18.   if Assigned(t.Ptr) then
  19.     Dispose(t.Ptr);
  20.   t.Ptr := nil;
  21. end;
  22.  
  23. var
  24.   Data: Array of TTest;
  25. begin
  26.   SetLength(Data, 3);
  27.   new(Data[1].Ptr);
  28.   Data[1].Ptr^ := 42;
  29.   SetLength(Data, 0);
  30. end.
This will not cause a memory leak because finalize is called on the records, which calls the repsective class operator, which frees the memory. Without this class operator, this would produce a memory leak

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Array Question
« Reply #11 on: May 05, 2022, 05:48:18 pm »
MarkMLI was faster...

I'd normally not comment on something like this, since there are many in this conf.- yourself included- better informed than I.

However I decided it was imperative to do so promptly after somebody- not OP obviously- introduced terminology that could be misconstrued.

Obviously your comments about finalisation are "the right way to do it": strings and dynamic arrays are implicitly /finalised/ when they go out of scope, and unlike- in particular- instances of classes don't need to be explicitly freed or destroyed.

I'm obviously reminded of the the work somebody (Gus?) put into trying to sort out the "object" vs "class" terminology, but don't have the link to hand.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Array Question
« Reply #12 on: May 05, 2022, 06:19:49 pm »
To go a bit further about what finalizing memory means. There are two kinds of types in Pascal, Managed types (managed records, COM interfaces, strings and arrays) and "simple" types.

Managed types need to be initialized and finalized. This is normally taken care of by the compiler implicetly. Simple types, don't need initialization and finalization. They might need some constructors or destructors (like objects and classes), but this is something different

A variable is located either on the stack, the heap or part of another block/variable (which than itself can be on the stack, heap, or part of another variable that can be on ...). There are also global variables, but let's just pretend they are on the stack for simplicity. The compiler tries to do the initialization and finalization implicetly. So if a new stack frame is entered, all variables on the stack are intialized, and when a stack frame is popped, all variables are finalised
Example:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch advancedrecords}
  5.  
  6. type
  7.   TManagedType = record
  8.     Data: Integer;
  9.     class operator Initialize(var t: TManagedType);
  10.     class operator Finalize(var t: TManagedType);
  11.   end;
  12.  
  13. class operator TManagedType.Initialize(var t: TManagedType);
  14. begin
  15.   WriteLn('Initializing TManagedType');
  16. end;
  17.  
  18. class operator TManagedType.Finalize(var t: TManagedType);
  19. begin
  20.   WriteLn('Finalizing TManagedType');
  21. end;
  22.  
  23. procedure Test;
  24. var
  25.   t: TManagedType;
  26. begin
  27.   t.Data := 42;
  28.   WriteLn('Leaving Function -> Popping stack frame');
  29. end;
  30.  
  31. begin
  32.   WriteLn('Entering Function -> Creating Stack Frame');
  33.   Test;
  34.   ReadLn;
  35. end.
Result:
Code: Pascal  [Select][+][-]
  1. Entering Function -> Creating Stack Frame
  2. Initializing TManagedType
  3. Leaving Function -> Popping stack frame
  4. Finalizing TManagedType
So when the function is entered, the stack frame is created, therefore the record variable "t" is initialized.
When the function exits, the stack frame is popped and the record variable "t" is finalised.

This is very easy for the compiler, it is like a hidden "try-finally" block. The second case that is easy for the compiler is the third case, when your variable is part of another variable, then the "child" variable is initialized whenever the parent variable is initialized and finalized when the parent is finalized:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch advancedrecords}
  5.  
  6. type  
  7.  
  8.   TManagedType2 = record
  9.     Data: Integer;
  10.     class operator Initialize(var t: TManagedType2);
  11.     class operator Finalize(var t: TManagedType2);
  12.   end;
  13.  
  14.   TManagedType = record
  15.     Data: TManagedType2;
  16.     class operator Initialize(var t: TManagedType);
  17.     class operator Finalize(var t: TManagedType);
  18.   end;
  19.  
  20. class operator TManagedType2.Initialize(var t: TManagedType2);
  21. begin
  22.   WriteLn('Initializing TManagedType2');
  23. end;
  24.  
  25. class operator TManagedType2.Finalize(var t: TManagedType2);
  26. begin
  27.   WriteLn('Finalizing TManagedType2');
  28. end;  
  29.  
  30. class operator TManagedType.Initialize(var t: TManagedType);
  31. begin
  32.   WriteLn('Initializing TManagedType');
  33. end;
  34.  
  35. class operator TManagedType.Finalize(var t: TManagedType);
  36. begin
  37.   WriteLn('Finalizing TManagedType');
  38. end;
  39.  
  40. procedure Test;
  41. var
  42.   t: TManagedType;
  43. begin
  44.   t.Data.Data := 42;
  45.   WriteLn('Leaving Function -> Popping stack frame');
  46. end;
  47.  
  48. begin
  49.   WriteLn('Entering Function -> Creating Stack Frame');
  50.   Test;
  51.   ReadLn;
  52. end.
  53.  
Code: Pascal  [Select][+][-]
  1. Entering Function -> Creating Stack Frame
  2. Initializing TManagedType2
  3. Initializing TManagedType
  4. Leaving Function -> Popping stack frame
  5. Finalizing TManagedType
  6. Finalizing TManagedType2
Same holds for classes, on create all "children" are initialized and on free all "children" are finalised:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch advancedrecords}
  5.  
  6. type  
  7.  
  8.   TManagedType = record
  9.     Data: Integer;
  10.     class operator Initialize(var t: TManagedType);
  11.     class operator Finalize(var t: TManagedType);
  12.   end;
  13.  
  14.   TTestClass = class
  15.   public
  16.     Data: TManagedType;
  17.     constructor Create;
  18.     destructor Destroy; override;
  19.   end;
  20.  
  21. constructor TTestClass.Create;
  22. begin
  23.   WriteLn('Creating class');
  24. end;
  25.  
  26. destructor TTestClass.Destroy;
  27. begin
  28.   inherited Destroy;
  29.   WriteLn('Destroying class');
  30. end;
  31.  
  32. class operator TManagedType.Initialize(var t: TManagedType);
  33. begin
  34.   WriteLn('Initializing TManagedType');
  35. end;
  36.  
  37. class operator TManagedType.Finalize(var t: TManagedType);
  38. begin
  39.   WriteLn('Finalizing TManagedType');
  40. end;
  41.  
  42. var
  43.   t: TTestClass;
  44. begin
  45.   t := TTestClass.Create;
  46.   t.Free;
  47.   ReadLn;
  48. end.
  49.  
Yields:
Code: Pascal  [Select][+][-]
  1. Initializing TManagedType
  2. Creating class
  3. Destroying class
  4. Finalizing TManagedType
  5.  
This is also the same for arrays, as shown above. If your variable is part of an array, when you increase the number of elements of an array, every new element will be initialised, and when you decrease the number of elements of an array, all elements will be finalised:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$ModeSwitch advancedrecords}
  5.  
  6. type  
  7.  
  8.   TManagedType = record
  9.     Data: Integer;
  10.     class operator Initialize(var t: TManagedType);
  11.     class operator Finalize(var t: TManagedType);
  12.   end;
  13.  
  14. class operator TManagedType.Initialize(var t: TManagedType);
  15. begin
  16.   WriteLn('Initializing TManagedType');
  17. end;
  18.  
  19. class operator TManagedType.Finalize(var t: TManagedType);
  20. begin
  21.   WriteLn('Finalizing TManagedType');
  22. end;
  23.  
  24. var
  25.   t: Array of TManagedType;
  26. begin
  27.   WriteLn('Adding 2 elements');
  28.   SetLength(t, 2);
  29.   WriteLn('Removing 1 element');
  30.   SetLength(t, 1);  
  31.   WriteLn('Removing all remaining (1) elements');
  32.   SetLength(t, 0);
  33.   ReadLn;
  34. end.
  35.  
Code: Pascal  [Select][+][-]
  1. Adding 2 elements
  2. Initializing TManagedType
  3. Initializing TManagedType
  4. Removing 1 element
  5. Finalizing TManagedType
  6. Removing all remaining (1) elements
  7. Finalizing TManagedType

The juicy bits come when you start dealing with the heap and with anonymous memory regions and pointers, but as I am currently on short time, I will leave that out for another time.

mas steindorff

  • Hero Member
  • *****
  • Posts: 532
Re: Array Question
« Reply #13 on: May 07, 2022, 05:25:14 am »
hi Warfley
I tried your code and got some errors.  I placed you examples in a separate unit
Code: [Select]
unit Unit1;

{$mode objfpc}{$H+}
{$ModeSwitch advancedrecords}

interface

uses
  Classes, SysUtils;

type
  TManagedType = record
    Data: Integer;
    needs_set: Boolean;
    class operator Initialize(var t: TManagedType);   
  end;

implementation

class operator TManagedType.Initialize(var t: TManagedType);
begin
  self.needs_set := true;
  self.Data:= 1;
end;

end.     

I get the following errors:
---
Compile Project, Target: DataSetTester.exe: Exit code 1, Errors: 2
Unit1.pas(15,30) Error: It is not possible to overload this operator. Related
  overloadable operators (if any) are:
Unit1.pas(15,51) Fatal: Syntax error, ":" expected but ";" found
---
is this due to my FPC = v2.0.0 ?

windows 10 &11, Ubuntu 21+ - fpc 3.0.4, IDE 2.0 general releases

Thausand

  • Sr. Member
  • ****
  • Posts: 292
Re: Array Question
« Reply #14 on: May 07, 2022, 08:30:42 am »
hi Warfley
I tried your code and got some errors.  I placed you examples in a separate unit
...
---
is this due to my FPC = v2.0.0 ?
Yes, https://wiki.freepascal.org/management_operators
Quote
From Free Pascal version 3.1.1 onwards, there is a new language feature called management operators for "extended" / "advanced" records.
more, https://wiki.freepascal.org/FPC_New_Features_3.2.0#Management_operators_for_record_types

 

TinyPortal © 2005-2018