[Res.] Why is enumerating a dynamic array different from accessing it by Index? lazarus / 61-Beginners ---------------------------------------------------------------------- ArminLinder · 2021-05-06 19:44 I found out (the hard way ... wasted long hours on this) that the following code snippets produce different results: These are the key parts showing the problem, a complete source is attached as well. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  PPayload = ^TPayload;   TPayload = record    ID: integer;    Name: string[50];  end;  TArrPayload = array of TPayload; var  ArrPayload: TArrPayload;  // ....................    // initalize the nodes   for i := low(ArrPayload) to high(ArrPayload) do  begin    VirtualStringTree1.AddChild(nil,@ArrPayload[i]);  end;   for rec in ArrPayload do     VirtualStringTree1.AddChild(nil, @rec);   VirtualStringTree.AddChild expects a pointer to data. The for ... in loop does deliver an invalid address, the for loop with integer index counter works correctly. Whatever rec contains, it is not, like I expected, a pointer to a record from the array. Anyone with deeper insight into FPC who can explain this? Thnx, Armin. Enclosed you find a little demo project to show the problem in context of a program, just in case anyone wants to see why I came to the conclusion that there must be a difference. The first 5 nodes are passed correctly, the other five contain a garbage address, and it's obviously 5 times the same address, since the garbage is always the same. Armin. ---------------------------------------------------------------------- ASerge · 2021-05-06 19:52 Quote from: Nimral on May 06, 2021, 09:44:16 pmI found out (the hard way ... wasted long hours on this) that the following code snippets produce different results: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for rec in ArrPayload doHere, rec is a local copy of the actual data. Saving the address of this variable is pointless. ---------------------------------------------------------------------- ArminLinder · 2021-05-06 19:57 I can assign like this, can't I? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for rec in ArrPayload do   rec.Name := 'Bla';  and this will modify the records in ArrPayload, right? So it *looks* like rec is an equivalent to ArrPayload[ i ], but it isn't? If it is not, what then is rec? Would there have been a way to pass the address of every record, like intended, using the for ... in enumeration mechanism? Armin. ---------------------------------------------------------------------- Remy Lebeau · 2021-05-06 20:06 Quote from: Nimral on May 06, 2021, 09:44:16 pmThe for ... in loop does deliver an invalid address No, it delivers a valid record, you are just taking the address of that record and expecting that address to outlive the actual lifetime of the record, which it doesn't. Quote from: Nimral on May 06, 2021, 09:44:16 pmWhatever rec contains, it is not, like I expected, a pointer to a record from the array. It is not a pointer to a record, no.  It is an actual instance of a record, that has been copied from the array. Quote from: Nimral on May 06, 2021, 09:44:16 pmThe first 5 nodes are passed correctly, the other five contain a garbage address, and it's obviously 5 times the same address, since the garbage is always the same. It is not garbage. The for..in loop is simply generating a hidden variable for its enumeration to use, and you are storing the address of that variable 5 times.  The variable doesn't exist anymore when the loop is done. Quote from: Nimral on May 06, 2021, 09:57:33 pmI can assign like this, can't I? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for rec in ArrPayload do   rec.Name := 'Bla'; and this will modify the records in ArrPayload, right? No, it will not, because ArrPayload is an array of records, so rec is a copy of each record in the array. Now, if you had an array of object pointers, that would be a different story... Quote from: Nimral on May 06, 2021, 09:57:33 pmSo it *looks* like rec is an equivalent to ArrPayload[ i ], but it isn't? If it is not, what then is rec? Have you read the documentation yet? https://www.freepascal.org/docs-html/ref/refsu58.html Essentially, your for..in loop is basically the same as doing this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  rec: TPayload;  i: Integer; for i := Low(ArrPayload) to High(ArrPayload) dobegin  rec := ArrPayload[i]; // <-- COPY MADE HERE!  rec.Name := 'Bla'; // <-- MODIFYING THE COPY, NOT THE ORIGINAL!  VirtualStringTree1.AddChild(nil, @rec); // <-- STORING THE ADDRESS OF THE COPY!end; // <-- COPY NOT ASSIGNED BACK TO ARRAY! ---------------------------------------------------------------------- ArminLinder · 2021-05-06 20:28 Thanks Remy, for the brief explanation. I did, however, assume (wrongly) then through the loop variable (rec) I get a "handle" to the current element of the enumerated structure, but it seems that the designers od Pascal had other ideas. What is the purpose of introducing a local copy then? It doesn't make much sense to me? And, yes, I flipped through the man pages, I found no information regarding this "local copy". It says (last sentence) that I may not change the loop variable (like it is forbidden to change the counter of a for ... to loop), but it doesn't say anything that it is also forbidden or at least pointless to change the data structure rec points to. Anyway, languages are defined the way they are, and not like it may seem logical to someone like me. Armin. ---------------------------------------------------------------------- lucamar · 2021-05-07 01:31 Quote from: Nimral on May 06, 2021, 10:28:01 pmWhat is the purpose of introducing a local copy then? It doesn't make much sense to me? Using enumerators (which is the "technical" name of that ... hmmm ... technic) in a for...in loop is basically a strategy, or a convenient way, to read through dynamic array-like structures without the need to know or care (as would be case with e.g. a for Low(x) to High(h)) for its size or indexing. That's it. As Remy explained, inside the for...in loop you deal with a copy of the contained object, so write operations are valid only for that copy, not for the original. But don't worry too much: this has been bugging out people all over the world since it was introduced ---------------------------------------------------------------------- egsuh · 2021-05-07 01:44 Try in following way. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  PPayload = ^TPayload;   TPayload = record    ID: integer;    Name: string[50];  end;   TArrPayload = array of PPayload;  // not array of TPayload var  ArrPayload: TArrPayload;  rec: PPayLoad;                  // define rec as pointer type, not record type  // ....................  for rec in ArrPayload do     VirtualStringTree1.AddChild(nil, rec);     //  rec itself is a pointer. BTW, I came to use VST through answering to your questions. So, do not thank me ---------------------------------------------------------------------- ArminLinder · 2021-05-07 06:42 @egsuh, welcome to the VST hell :-) 3+ Years of development by Mike Lischke alone, 2600 pages of pdf documentation, 32000+ lines of code, working with pointers, giving almost unlimited flexibility which leaves almost unlimited opportunities for hard to find glitches. Aside from this minor obstacles VST is a great and very versatile component :-) It took me about 3 weeks to get along with it, and I am still not quite done. Regarding your suggestion: did you test it using my little demo project linked above? I did a quick test, and it won't compile, Error "Got TPayload, expected pointer". IMHO, like Remy and others have outlined, rec is a local copy of the enumerated record, not a pointer, and adding its address to the tree will later, when you try to access the record via the stored address, create memory havoc, since the local copy of rec is long gone by then. My test program behaved just like that, so I assume that yours won't work. Greetings from rainy Bavaria, Armin. ---------------------------------------------------------------------- Thaddy · 2021-05-07 07:45 If you want to store... Always use packed arrays. Problem solved.. ---------------------------------------------------------------------- ArminLinder · 2021-05-07 07:49 Hm, how would a packed array help in this situation? Armin. Btw, you seem to be the most rapidy typing person in the world ...? See attachment :-) ---------------------------------------------------------------------- wp · 2021-05-07 08:27 Quote from: Nimral on May 07, 2021, 09:49:21 amBtw, you seem to be the most rapidy typing person in the world ...? See attachment :-) I think you are confusing decimal and thousand separators. ---------------------------------------------------------------------- egsuh · 2021-05-07 09:17 I have modified some of your codes, and it seems to work. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type    // ............    TArrPayload = array of PPayload;  // As I told, set this to array of pointers, not records     // ....... var    Form1: TForm1;    ArrPayload: TArrPayload;  implementation  procedure TForm1.FormCreate(Sender: TObject);const  NumItems = 5; var   i: integer;   rec: PPayload; begin    // populate a dynamic array    // ArrPayload := TArrPayload.Create;    SetLength(ArrPayLoad, NumItems); // I fixed length, but your approach would be nicer if the size is not known.      for i := 0 to High(ArrPayLoad) do    begin        New(rec);                 // You need to allocate memory to rec with New        rec^.ID := i;        rec^.Name := Format('Node %d', [i]);         ArrPayLoad[i] := rec;      //  rec, which is pointer is stored in the array.    end;     // initalize the nodes - want the adress of the records as payloads    VirtualStringTree1.NodeDataSize := SizeOf(PPayload);     {   // I commented out these     // these work ...,       for i := low(ArrPayload) to high(ArrPayload) do     begin          VirtualStringTree1.AddChild(nil,@ArrPayload[i]);    end;     //  }   //  following seems to work   for rec in ArrPayload do       VirtualStringTree1.AddChild(nil, rec); end; procedure TForm1.FormDestroy(Sender: TObject);var     rec: PPayLoad;begin     for rec in ArrPayLoad do Dispose(rec);  // You have to free memory, as these are using heap memory, not stack. end; ---------------------------------------------------------------------- Thaddy · 2021-05-07 10:53 The point of packed arrays is that you can store it at once. No iterations.... ---------------------------------------------------------------------- ArminLinder · 2021-05-07 11:06 I can't see how this behaviour is related to my problem. Would you please clarify? Armin ---------------------------------------------------------------------- PascalDragon · 2021-05-07 13:43 Quote from: Nimral on May 07, 2021, 09:49:21 amHm, how would a packed array help in this situation? They don't. Thaddy just provides usual nonsense help... As others already mentioned: best use the ordinary for-loop, the for … in-loop is not really designed for this.