Recent

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

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: Function to erase new items in an array
« Reply #15 on: March 16, 2018, 01:38:51 pm »
I think everything is initialized indeed, not only by examining assembler output:
Code: Pascal  [Select][+][-]
  1. program testit4;
  2. {$mode delphi}{$macro on}
  3. {$if fpc_fullversion < 30101}{$error this code needs fpc 3.1.1 or higher.}{$ifend}
  4.  
  5.   procedure setLength2<T>(var input: TArray<T>; size:integer);
  6.   var
  7.     a:integer;
  8.   begin
  9.     setLength(input,size);
  10.     for a := Low(input) to High(input) do input[a]:= Default(T);
  11.   end;
  12.  
  13.   function TestIfStackMatters:Boolean;
  14.   var
  15.     sa:Tarray<string>;
  16.     ia:TArray<integer>;
  17.     ua:TArray<dword>;
  18.     s :string;
  19.     i:integer;
  20.     u:dword;
  21.   begin
  22.     Setlength(sa, 8);
  23.     Setlength(ia, 9);
  24.     Setlength(ua, 10);
  25.     writeln(Length(sa):4,Length(ia):4,length(ua):4);
  26.     for s in sa do write(s:4);// should be empty line;
  27.     writeln;
  28.     for i in ia do write(i:2);// should be nine zero's
  29.     writeln;
  30.     for u in ua do write(u:2);// should be ten zero's
  31.     writeln;
  32.     // not and exhaustive test, but indicative if the arrays are initialized.
  33.     result := not ((sa[High(sa)] ='') and (ia[High(ia)] = 0) and (ua[High(ua)]=0));
  34.   end;
  35.  
  36. begin
  37.   writeln(TestIfStackMatters);  
  38. end.

Anyway: generics are way better and *a lot* faster than variants. procedure body stays the same.

@Tommi this also works with your records.
« Last Edit: March 16, 2018, 01:48:50 pm by Thaddy »
Specialize a type, not a var.

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Function to erase new items in an array
« Reply #16 on: March 16, 2018, 05:52:22 pm »
@Tommi, forget about generics and helper functions. As already mentioned, when the sizes of dynamic arrays change, the new elements are reset to zero. This is even indicated in the documentation for the SetLength function. Those for structures that contain reference types (other arrays and strings) this means that they will all be of zero length.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Function to erase new items in an array
« Reply #17 on: March 16, 2018, 08:05:13 pm »
@Tommi, forget about generics and helper functions. As already mentioned, when the sizes of dynamic arrays change, the new elements are reset to zero. This is even indicated in the documentation for the SetLength function. Those for structures that contain reference types (other arrays and strings) this means that they will all be of zero length.
Yeah, using dynamic arrays only is very lightweight solution, but you should remember, that memory operations aren't free and therefore you should pre-allocate some elements in order not to reallocate memory way too often. And therefore you should store real count and so on... I.e. may be it's better to use proper class for this task, i.e. TList<T> generic?
« Last Edit: March 16, 2018, 08:08:43 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?

Tommi

  • Full Member
  • ***
  • Posts: 213
Re: Function to erase new items in an array
« Reply #18 on: March 16, 2018, 08:28:00 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

OK, maybe that has been some change in dyn arrays managment between 1.6(or earlier) and 1.8 lazarus ?

Sometime I had problems about code not everytime working at the same. It could happened that on a PC the code was working and on another not. But it was the same code working on the same data.

Initialiazing dynamic arrays the problems disappeared.

Since then I always initialize my dynamic arrays and no more problem.

Unfortunately at the moment I haven't any code to provide as example. I am going to try to provide it.

I prefer dyn arrays over classes because there is minor risks about memory leaks. Specially in try... except blocks.
« Last Edit: March 16, 2018, 08:31:44 pm by Tommi »

Tommi

  • Full Member
  • ***
  • Posts: 213
Re: Function to erase new items in an array
« Reply #19 on: March 16, 2018, 08:47:26 pm »
I am not the only one having this problem.
This is the post of howardpc taken from here:
http://forum.lazarus.freepascal.org/index.php/topic,38392.0.html


At first glance i'd say that your problem is that "myArray" is not initialized, so that it can have a random initial length when you don't add "setLength(myArray,0);" at the beginning.

This is indeed the principal problem.
FPC sets the length of most dynamic arrays (including strings) on initialization to zero reliably.
However, for dynamic arrays of Integer and Byte (and possibly other types) the array Length() after (compiler) initialization is non-zero. In my testing it is not random, but this may just be an artefact of my system, and anyway is undocumented and presumably implementation-dependent.
The bottom line is that all dynamic arrays, and particularly arrays of Byte or arrays of Integer should be initialised by the programmer either by assigning Nil to the array or using an explicit SetLength(xxx, 0).

Thaddy

  • Hero Member
  • *****
  • Posts: 14204
  • Probably until I exterminate Putin.
Re: Function to erase new items in an array
« Reply #20 on: March 16, 2018, 08:54:20 pm »
Tommi I showed you how to do that, using Default()....  O:-) And superfluous as Mr.MadGuy explained.

ASerge is wrong: it is done for you in any and all cases:
Code: Pascal  [Select][+][-]
  1. program untitled;
  2. var
  3.  a:array of integer;
  4.  i:integer;
  5. begin
  6.  a := a.Create(1,2,3,4,5,6,7,8,9);
  7.  for i in a do writeln(i);
  8.  setlength(a, 20);  // or 1000 or 20000....
  9.  for i in a do writeln(i);
  10. end.

You can fold this into a procedure or function (so force stack allocation)  and it will still be initialised for ANY type.

And don't forget about generics: much better than variants. And an integer is NOT a managed type.
« Last Edit: March 16, 2018, 09:28:00 pm by Thaddy »
Specialize a type, not a var.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Function to erase new items in an array
« Reply #21 on: March 17, 2018, 06:58:57 am »
I am not the only one having this problem.
This is the post of howardpc taken from here:
http://forum.lazarus.freepascal.org/index.php/topic,38392.0.html
First you need to check two things:
1) That your code doesn't have heap corruptions, i.e. that you don't write any data to out of bounds elements, etc.
2) That you don't implicitly assign or copy one dynamic array variable to another (especially if you use records) - they're actually pointers and therefore data, they refer to, is shared, so when you modify one array - other may become modified too.

I.e. all you need - is to understand, how they work, and use them properly.

I constantly use dynamic arrays both in Delphi and Lazarus and I've never encountered any problems with them.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Function to erase new items in an array
« Reply #22 on: March 17, 2018, 12:03:37 pm »
ASerge is wrong...
@Thaddy are you all right  :o? You wrote that I was wrong and brought the code that underwrites my words 8).

Tommi

  • Full Member
  • ***
  • Posts: 213
Re: Function to erase new items in an array
« Reply #23 on: March 17, 2018, 04:33:48 pm »
Ok thank you everyone for your help :)

 

TinyPortal © 2005-2018