Recent

Author Topic: Function to erase new items in an array  (Read 9204 times)

Tommi

  • Full Member
  • ***
  • Posts: 213
Function to erase new items in an array
« on: March 16, 2018, 08:06:33 am »
I'd like to use a function like this:

Code: [Select]
procedure setLength2(var input: array of variant; size:integer);
var
   a,b:integer;
begin
   a:=length(input);
   setLength(input,size);
   for b := a to size-1 do
   begin
     fillChar(input[b],sizeOf(input[b]),0);
   end;
end;

But it doesn't compile because resizing of dynamic arrays passed as parameter isn't allowed.

How could I do ?

Thank you

balazsszekely

  • Guest
Re: Function to erase new items in an array
« Reply #1 on: March 16, 2018, 08:10:26 am »
Try this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TAV = array of variant;
  3.  
  4. procedure setLength2(var input: TAV; size:integer);
  5. var
  6.    a,b:integer;
  7. begin
  8.    a:=length(input);
  9.    setLength(input,size);
  10.    for b := a to size-1 do
  11.    begin
  12.      fillChar(input[b],sizeOf(input[b]),0);
  13.    end;
  14. end;
  15.  
  16. procedure TForm1.Button1Click(Sender: TObject);
  17. var
  18.   av: TAV;
  19. begin
  20.   setLength2(av, 5);
  21. end;                  

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Function to erase new items in an array
« Reply #2 on: March 16, 2018, 08:28:32 am »
I'd like to use a function like this:

Code: [Select]
procedure setLength2(var input: array of variant; size:integer);
var
   a,b:integer;
begin
   a:=length(input);
   setLength(input,size);
   for b := a to size-1 do
   begin
     fillChar(input[b],sizeOf(input[b]),0);
   end;
end;

But it doesn't compile because resizing of dynamic arrays passed as parameter isn't allowed.

How could I do ?

Thank you
This is one of very confusing things in Pascal. This two are completely different things:
1) Open array:
Code: Pascal  [Select][+][-]
  1. procedure Proc(A:array of Integer);
  2.  
2) Dynamic array:
Code: Pascal  [Select][+][-]
  1. type
  2.    TArr = array of Integer;
  3.  
  4. procedure Proc(A:TArr);
  5.  

P.S. As I remember, dynamic arrays are initialized automatically. And your code is wrong anyway. It should be like this:
Code: Pascal  [Select][+][-]
  1. A := Length(Input);
  2. SetLength(Input, Size);
  3. if Size > A then
  4.    FillChar(Input[A], SizeOf(Input[A]) * (Size - A), 0);
  5.  
« Last Edit: March 16, 2018, 08:33:38 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: Function to erase new items in an array
« Reply #3 on: March 16, 2018, 08:32:59 am »
Code: Pascal  [Select][+][-]
  1. program testit;
  2. {$mode objfpc}
  3. uses variants;
  4. type
  5.   TVariantArray = Array of Variant;
  6. procedure setLength2(var input: TVariantArray; size:integer);
  7. var
  8.    a,b:integer;
  9. begin
  10.    a:=length(input);
  11.    setLength(input,size);
  12.    for b := a to size-1 do
  13.    begin
  14.      fillChar(input[b],sizeOf(input[b]),0);
  15.    end;
  16. end;
  17. begin
  18. end
.

Posts crossed.. Sorry.
Reason is that a parameter as array of variant is not the same as a dynamic array of variant.....  It is an open array of variant!
So you need to declare a type for it.

See https://freepascal.org/docs-html/ref/refsu68.html#x180-20200014.4.5

« Last Edit: March 16, 2018, 08:39:33 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Tommi

  • Full Member
  • ***
  • Posts: 213
Re: Function to erase new items in an array
« Reply #4 on: March 16, 2018, 09:15:07 am »
The reason because I put "array of variant" is that I want make that function working with all types.

So, for example:
Code: [Select]
var
 TStringArray:array of string;
 TIntArray:array of integer;
 TBolArray:array of boolean;
 TIntArrayArray:array of array of integer;
begin
 setLength2(TStringArray,10);   
 setLength2(TIntArray,10);
 setLength2(TBolArray,10);
 setLength2(TIntArrayArray,10);
end;

It should works without overloading any single type, is it possible ?

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Function to erase new items in an array
« Reply #5 on: March 16, 2018, 09:32:44 am »
The reason because I put "array of variant" is that I want make that function working with all types.

So, for example:
Code: [Select]
var
 TStringArray:array of string;
 TIntArray:array of integer;
 TBolArray:array of boolean;
 TIntArrayArray:array of array of integer;
begin
 setLength2(TStringArray,10);   
 setLength2(TIntArray,10);
 setLength2(TBolArray,10);
 setLength2(TIntArrayArray,10);
end;

It should works without overloading any single type, is it possible ?
Biggest question: why do you need it in a first place?

Code: Pascal  [Select][+][-]
  1. type
  2.   TMyArray = array of Integer;
  3.  
  4. function ArrayToStr(A:TMyArray):String;
  5.   var I:Integer;
  6. begin
  7.   Result := '';
  8.   for I := 0 to Length(A) - 1 do begin
  9.     if I > 0 then Result := Result + ', ';
  10.     Result := Result + IntToStr(A[I]);
  11.   end;
  12. end;
  13.  
  14. procedure TForm1.FormCreate(Sender: TObject);
  15.   var I:Integer;
  16.   A:TMyArray;
  17. begin
  18.   SetLength(A, 10);
  19.   Memo1.Lines.Add(ArrayToStr(A));
  20.   for I := 0 to 9 do begin
  21.     A[I] := I + 1;
  22.   end;
  23.   Memo1.Lines.Add(ArrayToStr(A));
  24.   SetLength(A, 5);
  25.   Memo1.Lines.Add(ArrayToStr(A));
  26.   SetLength(A, 10);
  27.   Memo1.Lines.Add(ArrayToStr(A));
  28. end;                
  29.  
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Tommi

  • Full Member
  • ***
  • Posts: 213
Re: Function to erase new items in an array
« Reply #6 on: March 16, 2018, 09:42:58 am »
Yes but your code is not reliable. Sometime may work and sometime no.

http://forum.lazarus.freepascal.org/index.php/topic,38392.0.html

Also note: between two calls, the local variables don't share the same memory (it can happens though). A new stack frame is allocated for each call. What can happen is that the heap memory used by a stack variable (such as your array) get reused, but it's UB. You cant rely on that.

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: Function to erase new items in an array
« Reply #7 on: March 16, 2018, 10:00:55 am »
Here's one using generics (faster than variants, much faster:
Code: Pascal  [Select][+][-]
  1. program testit;
  2. {$mode objfpc}
  3. type
  4.   generic TMyArray<T> = array of T;  
  5. generic procedure setLength2<T>(var input: specialize TMyArray<T>; size:integer);
  6. var
  7.    a,b:integer;
  8. begin
  9.    a:=length(input);
  10.    setLength(input,size);
  11.    for b := a to size-1 do
  12.    begin
  13.      fillChar(input[b],sizeOf(input[b]),0);
  14.    end;
  15. end;
  16.  
  17. var
  18.  sa:specialize TMyarray<string>;
  19.  ia:specialize TMyArray<integer>;
  20.  ua:specialize TMyArray<dword>;
  21. begin
  22.  specialize Setlength2<string>(sa, 8);
  23.  specialize Setlength2<integer>(ia, 9);
  24.  specialize Setlength2<dword>(ua, 10);
  25.  writeln(Length(sa):4,Length(ia):4,length(ua):4);
  26. end.

I will add a Delphi syntax version later.
[edit delphi mode]

Code: Pascal  [Select][+][-]
  1. program testit2;
  2. {$mode delphi}
  3. type
  4.   TMyArray<T> = array of T;  
  5.   procedure setLength2<T>(var input: TMyArray<T>; size:integer);
  6. var
  7.    a,b:integer;
  8. begin
  9.    a:=length(input);
  10.    setLength(input,size);
  11.    for b := a to size-1 do
  12.    begin
  13.      fillChar(input[b],sizeOf(input[b]),0);
  14.    end;
  15. end;
  16.  
  17. var
  18.  sa:TMyarray<string>;
  19.  ia:TMyArray<integer>;
  20.  ua:TMyArray<dword>;
  21. begin
  22.  Setlength2<string>(sa, 8);
  23.  Setlength2<integer>(ia, 9);
  24.  Setlength2<dword>(ua, 10);
  25.  writeln(Length(sa):4,Length(ia):4,length(ua):4);
  26. end.
« Last Edit: March 16, 2018, 10:03:36 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Function to erase new items in an array
« Reply #8 on: March 16, 2018, 10:01:59 am »
Yes but your code is not reliable. Sometime may work and sometime no.

http://forum.lazarus.freepascal.org/index.php/topic,38392.0.html

Also note: between two calls, the local variables don't share the same memory (it can happens though). A new stack frame is allocated for each call. What can happen is that the heap memory used by a stack variable (such as your array) get reused, but it's UB. You cant rely on that.
Dynamic arrays work exactly the same way, as strings - they're managed automatically, i.e. are initialized at the beginning of procedure and freed at the end. If it doesn't happen, then something is wrong with your code - some sort of heap corruption happens.

Simple way to prove it - just debug code. You can clearly see FillChar call after GetMem, so you don't have to call FillChar explicitly.
« Last Edit: March 16, 2018, 10:18:31 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: Function to erase new items in an array
« Reply #9 on: March 16, 2018, 10:14:46 am »
@Tommi: this is one that uses trunk features:
Code: Pascal  [Select][+][-]
  1. program testit3;
  2. {$mode delphi}{$macro on}
  3. {$if fpc_fullversion < 30101}{$error this code needs fpc 3.1.1 or higher.}{$ifend}
  4.   procedure setLength2<T>(var input: TArray<T>; size:integer);
  5.   var
  6.     a,b:integer;
  7.   begin
  8.     a:=length(input);
  9.     setLength(input,size);
  10.     for b := a to size-1 do
  11.     begin
  12.      fillChar(input[b],sizeOf(input[b]),0);
  13.     end;
  14.   end;
  15.  
  16. var
  17.   sa:Tarray<string>;
  18.   ia:TArray<integer>;
  19.   ua:TArray<dword>;
  20. begin
  21.   Setlength2<string>(sa, 8);
  22.   Setlength2<integer>(ia, 9);
  23.   Setlength2<dword>(ua, 10);
  24.   writeln(Length(sa):4,Length(ia):4,length(ua):4);
  25. end.

So you really do not have to use variants anymore. My earlier examples should also compile in fpc 3.0.4.
Difference is TArray<T> is now in system.pas
« Last Edit: March 16, 2018, 11:17:23 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Tommi

  • Full Member
  • ***
  • Posts: 213
Re: Function to erase new items in an array
« Reply #10 on: March 16, 2018, 11:23:56 am »
Thank you very much Thaddy :)

Reading post from Mr.Madguy the question is: it is really necessary ?

I ask this because in the past I have occasionally found bugs that I solved zeroing the array. (Usually array of records or array of array).
But studying asm code in effect we can see a FILLCHARS call, and also a fpc_dynarray_clear.
« Last Edit: March 16, 2018, 11:25:51 am by Tommi »

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: Function to erase new items in an array
« Reply #11 on: March 16, 2018, 11:29:50 am »
Well, if you are unsure of the type being maintained or not, you can use the Default() compiler intrinsic to initialize any type of the elements in the dynamic array.
e.g.:
Code: Pascal  [Select][+][-]
  1.   procedure setLength2<T>(var input: TArray<T>; size:integer);
  2.   var
  3.     a:integer;
  4.   begin
  5.     setLength(input,size);
  6.     for a := Low(input) to High(input) do input[a]:= Default(T);
  7.   end;

That's also safer, now I looked at it. (Don't call default on the array itself:it will be empty, so call it on the elements)

Anyway: setlength will initialize memory on growth, as explained by others, so it is superfluous. It will cut-off on shrink and preserve.
Note stack allocated types that are unmanaged may benefit from such code because the stack is dirty. But then again dynamic arrays are cleaned.
« Last Edit: March 16, 2018, 11:56:13 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Tommi

  • Full Member
  • ***
  • Posts: 213
Re: Function to erase new items in an array
« Reply #12 on: March 16, 2018, 12:01:11 pm »
Basically my issue is that I have to use very often this kind of structure:

Code: [Select]
type myrecord1=record
  aa:array of string;
  bb:array of array of integer;
  cc:array of boolean;
  dd:string;
end;

type myArray=array of myrecord1;

....

....
var
  a:myArray;
begin
  setLength(a,0);
  while mycondition do
  begin
   
    setLength(A,length(A)+1);
    A[length(A)-1].dd:='Test';
  end;
end;

At the moment every time I have to do something like this:
Code: [Select]
   setLength(A,length(A)+1);
   setLength(A[length(A)-1].aa,0);   //Boring and dangerous line
   setLength(A[length(A)-1].bb,0);   //Boring and dangerous line
   setLength(A[length(A)-1].cc,0);    //Boring and dangerous line
   A[length(A)-1].dd:='Test';

The "Boring and dangerous lines" are easy sources of bugs.

What I would like to have is that when I do  setLength(A,length(A)+1) , every subArray length is set to 0 and not to a random value.

I think that with your code I should obtain this, is it ?

Thaddy

  • Hero Member
  • *****
  • Posts: 14377
  • Sensorship about opinions does not belong here.
Re: Function to erase new items in an array
« Reply #13 on: March 16, 2018, 12:14:58 pm »
Yes. You can even limit the generic type to record:  TArray<T:record> .
And subsequently use Default(TMyRecord) over the iteration.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Function to erase new items in an array
« Reply #14 on: March 16, 2018, 12:34:30 pm »
Basically my issue is that I have to use very often this kind of structure:

Code: [Select]
type myrecord1=record
  aa:array of string;
  bb:array of array of integer;
  cc:array of boolean;
  dd:string;
end;

type myArray=array of myrecord1;

....

....
var
  a:myArray;
begin
  setLength(a,0);
  while mycondition do
  begin
   
    setLength(A,length(A)+1);
    A[length(A)-1].dd:='Test';
  end;
end;

At the moment every time I have to do something like this:
Code: [Select]
   setLength(A,length(A)+1);
   setLength(A[length(A)-1].aa,0);   //Boring and dangerous line
   setLength(A[length(A)-1].bb,0);   //Boring and dangerous line
   setLength(A[length(A)-1].cc,0);    //Boring and dangerous line
   A[length(A)-1].dd:='Test';

The "Boring and dangerous lines" are easy sources of bugs.

What I would like to have is that when I do  setLength(A,length(A)+1) , every subArray length is set to 0 and not to a random value.

I think that with your code I should obtain this, is it ?
You must be doing something wrong. I suspect, that you have heap corruption somewhere in your code.

My code is:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TMyRecord = record
  15.     A:array of Integer;
  16.     B:array of String;
  17.     C:array of Boolean;
  18.   end;
  19.  
  20.   TMyArray = array of TMyRecord;
  21.  
  22.   TForm1 = class(TForm)
  23.     Memo1: TMemo;
  24.     procedure FormCreate(Sender: TObject);
  25.   private
  26.     { private declarations }
  27.   public
  28.     { public declarations }
  29.     procedure FillArray;
  30.   end;
  31.  
  32. var
  33.   Form1: TForm1;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. { TForm1 }
  40.  
  41. procedure TForm1.FillArray;
  42.   var V:TMyArray;
  43.   I, J:Integer;
  44. begin
  45.   Memo1.Lines.Add('Fill start');
  46.   for I := 0 to 9 do begin
  47.     SetLength(V, Length(V) + 1);
  48.     with V[Length(V) - 1] do begin
  49.       Memo1.Lines.Add(
  50.         IntToStr(Length(A)) + ' ' +
  51.         IntToStr(Length(B)) + ' ' +
  52.         IntToStr(Length(C))
  53.       );
  54.       SetLength(A, 10);
  55.       SetLength(B, 10);
  56.       SetLength(C, 10);
  57.       for J := 0 to 9 do begin
  58.         A[J] := Random(10) + 1;
  59.         B[J] := IntToStr(A[J]);
  60.         C[J] := (A[J] and 1) = 1;
  61.       end;
  62.     end;
  63.   end;
  64.   Memo1.Lines.Add('Fill end');
  65.   Memo1.Lines.Add('Test begin');
  66.   for I := 0 to Length(V) - 1 do begin
  67.     with V[I] do begin
  68.       Memo1.Lines.Add(
  69.         IntToStr(Length(A)) + ' ' +
  70.         IntToStr(Length(B)) + ' ' +
  71.         IntToStr(Length(C))
  72.       );
  73.     end;
  74.   end;
  75.   Memo1.Lines.Add('Test end');
  76. end;
  77.  
  78. procedure TForm1.FormCreate(Sender: TObject);
  79.   var I:Integer;
  80. begin
  81.   for I := 0 to 9 do begin
  82.     Memo1.Lines.Add('Test #' + IntToStr(I + 1));
  83.     FillArray;
  84.   end;
  85. end;
  86.  
  87. end.
  88.  
Working, as intended. Initialized by 0 at beginning of test and everything is 10 at the end. No errors.
Code: [Select]
Test #1
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #2
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #3
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #4
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #5
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #6
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #7
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #8
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #9
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
Test #10
Fill start
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
Fill end
Test begin
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
Test end
« Last Edit: March 16, 2018, 12:38:51 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

 

TinyPortal © 2005-2018