Recent

Author Topic: [SOLVED] IndexDWord behavior on a dynamic DWord array  (Read 335 times)

PascalJulius

  • Newbie
  • Posts: 2
[SOLVED] IndexDWord behavior on a dynamic DWord array
« on: January 09, 2026, 05:28:18 pm »
Hi! I'm new here and I am learning FreePascal by means of Advent of Code challenges. On a lot of challenges, I used my own IndexOf<Integer> function, but I was very happy to find out that IndexWord, IndexDWord, and IndexQWord are already standard.

There is some behavior I find very confusing. I tried switching to IndexDWord:

Code: Pascal  [Select][+][-]
  1. type
  2.   // adjacency table
  3.   TConnectionTable = array of array of Boolean;
  4.  
  5. procedure FindConnected(RootIndex : DWord; Conns : TConnectionTable; var Visited : specialize TArray<DWord>);
  6. var
  7.   I : DWord;
  8. begin
  9.   Insert(RootIndex, Visited, Length(Visited)); // Mark root as "visited"
  10.  
  11.   for I := Low(Conns) to High(Conns) do
  12.  
  13.     // Search for connections to the root
  14.     // but only add them if they are not yet "visited"
  15.     if Conns[I][RootIndex] and (IndexDWord(Visited, Length(Visited), I) = -1) then
  16.       // recurse
  17.       FindConnected(I, Conns, Visited);
  18. end;
  19.  

I'm using IndexDWord to check if a node with id I is already visited. But the above code results in a SEGFAULT. It only seems to work if I change

Code: Pascal  [Select][+][-]
  1. IndexDWord(Visited, Length(Visited), I)
  2.  

to this altered expression

Code: Pascal  [Select][+][-]
  1. IndexDWord(Visited[0], Length(Visited), I)
  2.  

and I don't understand why this is the case. All the example code IndexDWord passes the array itself. But I can only get correct behavior if I pass the first element. Please enlighten me :)
« Last Edit: January 09, 2026, 07:47:32 pm by PascalJulius »

runewalsh

  • Full Member
  • ***
  • Posts: 109
Re: IndexDWord behavior on a dynamic DWord array
« Reply #1 on: January 09, 2026, 05:59:41 pm »
Index* functions accept their first argument as “untyped const” which is a concept unique to Pascal but, if you know C++, can be thought as const void& (non-existent in C++). In C++, &-references are disguised pointers that, when used as functions arguments, automatically take the address of the passed variable. In Pascal, dynamic arrays are physically pointers, so when passed into the “untyped const”, the address of this pointer variable is taken, not the address of the contents it points to.

Visited[0] is a way to dereference said pointer and pass contents it points to, but I’d recommend Pointer(Visited)^ because Visited[0] triggers a range check error on empty Visited array. OTOH, Visited[0] will work with a static array without modifications, so the choice is up to you.

Try this:
Code: Pascal  [Select][+][-]
  1. procedure F_untyped(const data);
  2. begin
  3.         writeln('F_untyped got pointer @', HexStr(@data));
  4. end;
  5.  
  6. var
  7.         a: array of byte;
  8.  
  9. begin
  10.         SetLength(a, 5);
  11.         writeln('Address of A is ', HexStr(@a));
  12.         writeln('A pointer value is ', HexStr(pointer(a)));
  13.         writeln('Address of A contents is ', HexStr(@a[0]));
  14.  
  15.         writeln(LineEnding + 'Calling F_untyped(a)');
  16.         F_untyped(a);
  17.  
  18.         writeln(LineEnding + 'Calling F_untyped(a[0])');
  19.         F_untyped(a[0]);
  20.  
  21.         writeln(LineEnding + 'Calling F_untyped(pointer(a)^)');
  22.         F_untyped(pointer(a)^);
  23. end.
  24.  

PascalJulius

  • Newbie
  • Posts: 2
Re: IndexDWord behavior on a dynamic DWord array
« Reply #2 on: January 09, 2026, 06:09:11 pm »
Index* functions accept their first argument as “untyped const” which is a concept unique to Pascal but, if you know C++, can be thought as const void& (non-existent in C++). In C++, &-references are disguised pointers that, when used as functions arguments, automatically take the address of the passed variable. In Pascal, dynamic arrays are physically pointers, so when passed into the “untyped const”, the address of this pointer variable is taken, not the address of the contents it points to.

Ohhh that explains it! And now I finally comprehend what that const buf means in the `IndexDWord` signature. So `buf` is passed by reference, which means my original invocation was using the address of the pointer instead of the contents of the array. That explains everything! The C++ comparison is super helpful.

Visited[0] is a way to dereference said pointer and pass contents it points to, but I’d recommend Pointer(Visited)^ because Visited[0] triggers a range check error on empty Visited array. OTOH, Visited[0] will work with a static array without modifications, so the choice is up to you.

I will use your Pointer(Visited)^ from now on so that it will be more correct for empty arrays. Thanks a bunch!

 

TinyPortal © 2005-2018