Trying to understand TStringDynArray

I've been away from programming a while.  While coding a simple project, 
I've come across TStringDynArray.  The below code snippet I've entered 
magically works (I'm using XE4), no errors, no warnings, no hints.

In the (distant) past, I recall having to set the length of a dynamic 
array.  That's no longer needed?  Am I creating some memory leak with 
the below code?  No need to free up memory?

Thanks,

Bob Lincoln

==================================================


procedure TForm1.btnZipFolderClick(Sender: TObject);
var
   DirToZip: string;
   ZipList: TStringDynArray;
   I: integer;
begin

   DirToZip := 'C:\Users\My Name\Desktop\My Folder';

   ZipList := TDirectory.GetFiles(DirToZip, '*', 
TSearchOption.soAllDirectories);

   for I := 0 to Length(ZipList) do
     ListBox1.Items.Add(ZipList[I]);

end;
0
Robert
9/2/2013 7:08:12 PM
embarcadero.delphi.general 4258 articles. 0 followers. Follow

16 Replies
19024 Views

Similar Articles

[PageSpeed] 18

Actually, the for loop should be:

   for I := 0 to Length(ZipList) - 1 do
     ListBox1.Items.Add(ZipList[I]);


On 9/2/2013 12:08 PM, Robert Lincoln wrote:
> I've been away from programming a while.  While coding a simple project,
> I've come across TStringDynArray.  The below code snippet I've entered
> magically works (I'm using XE4), no errors, no warnings, no hints.
>
> In the (distant) past, I recall having to set the length of a dynamic
> array.  That's no longer needed?  Am I creating some memory leak with
> the below code?  No need to free up memory?
>
> Thanks,
>
> Bob Lincoln
>
> ==================================================
>
>
> procedure TForm1.btnZipFolderClick(Sender: TObject);
> var
>     DirToZip: string;
>     ZipList: TStringDynArray;
>     I: integer;
> begin
>
>     DirToZip := 'C:\Users\My Name\Desktop\My Folder';
>
>     ZipList := TDirectory.GetFiles(DirToZip, '*',
> TSearchOption.soAllDirectories);
>
>     for I := 0 to Length(ZipList) do
>       ListBox1.Items.Add(ZipList[I]);
>
> end;
>
0
Robert
9/2/2013 7:24:38 PM
Robert wrote:

> In the (distant) past, I recall having to set the length of a
> dynamic array.

If you are allocating the array yourself, then yes.  That is not the case 
in your example.

> Am I creating some memory leak with the below code?
> No need to free up memory?

No, you are not creating a memory leak.  TDirectory.GetFiles() allocates 
and returns its own dynamic array of strings, which you are then taking ownership 
of.  A dynamic array is reference counted.  When your local ZipList variable 
goes out of scope, the array will be freed because its reference count falls 
to 0.

--
Remy Lebeau (TeamB)
0
Remy
9/2/2013 7:35:27 PM
Robert Lincoln wrote:

> I've been away from programming a while.  While coding a simple
> project, I've come across TStringDynArray.  The below code snippet
> I've entered magically works (I'm using XE4), no errors, no warnings,
> no hints.
> 
> In the (distant) past, I recall having to set the length of a dynamic 
> array.  That's no longer needed?  Am I creating some memory leak with 
> the below code?  No need to free up memory?

There is no magic involved here. TStringDynArray is an array of string,
and the code behaves exactly as if it were declared with ZipList: array
of string. The TDirectory.GetFiles function returns a dynamic array it
has sized and filled with the files to return internally. And like for
any other dynamic array the compiler adds code to make sure the memory
for the array is released when the array variable goes out of scope.

> procedure TForm1.btnZipFolderClick(Sender: TObject);
> var
>    DirToZip: string;
>    ZipList: TStringDynArray;
>    I: integer;
> begin
>    DirToZip := 'C:\Users\My Name\Desktop\My Folder';
>    ZipList := TDirectory.GetFiles(DirToZip, '*',
TSearchOption.soAllDirectories);
>    for I := 0 to Length(ZipList) do
>      ListBox1.Items.Add(ZipList[I]);


-- 
Peter Below (TeamB)
0
Peter
9/2/2013 7:36:06 PM
Robert wrote:

> Actually, the for loop should be:
> 
> for I := 0 to Length(ZipList) - 1 do
> ListBox1.Items.Add(ZipList[I]);

You can alternatively use a for..in loop instead:

{code:delphi}
var
  filename: string;

for filename in ZipList do
  ListBox1.Items.Add(filename);
{code}

--
Remy Lebeau (TeamB)
0
Remy
9/2/2013 7:36:20 PM
Peter wrote:

> The TDirectory.GetFiles function returns a dynamic array it has sized and
> filled with the files to return internally.

An array that is sized quite inefficiently, I might add - it is resized/reallocated 
on every single file that is found.  Embarcadero should know better than 
to do that on (potentially) large lists.  I would think it would be better 
to add the files to a TStringList first, and then allocate+fill the final 
array only once from that.  And then maybe they should forget the array altogether 
and just return the TStringList itself (or let the caller pass in a TStrings 
to be filled).  Sometimes I worry about what Embarcadero designers are thinking 
when they design public interfaces with inefficient implementations.

--
Remy Lebeau (TeamB)
0
Remy
9/2/2013 8:46:49 PM
Peter,

| There is no magic involved here. TStringDynArray is an array of
| string, and the code behaves exactly as if it were declared with
| ZipList: array of string. The TDirectory.GetFiles function returns a
| dynamic array it has sized and filled with the files to return
| internally.

Do you know if XE and later know how to handle both Unicode and ANSI
strings in TStringDynArray?  (I'm to busy at the moment to play with
it. <g>)

-- 

   Q 

09/02/2013 13:56:09

1.19.1.372  [Q'sBrokenToolBar] [Running on TQ]
0
Quentin
9/2/2013 8:58:49 PM
Where can one find the code for TDirectory.GetFiles?  When I look in 
System.IOUtils.pas, I just see a call to GetFiles().  Where does that 
come from?

In other words, how can one know:

"... it is resized/reallocated on every single file that is found."


Also, thanks for everyone's responses so quickly.  I obviously need to 
go back and relearn some things.

Bob Lincoln





On 9/2/2013 1:46 PM, Remy Lebeau (TeamB) wrote:
> Peter wrote:
>
>> The TDirectory.GetFiles function returns a dynamic array it has sized and
>> filled with the files to return internally.
>
> An array that is sized quite inefficiently, I might add - it is resized/reallocated
> on every single file that is found.  Embarcadero should know better than
> to do that on (potentially) large lists.  I would think it would be better
> to add the files to a TStringList first, and then allocate+fill the final
> array only once from that.  And then maybe they should forget the array altogether
> and just return the TStringList itself (or let the caller pass in a TStrings
> to be filled).  Sometimes I worry about what Embarcadero designers are thinking
> when they design public interfaces with inefficient implementations.
>
> --
> Remy Lebeau (TeamB)
>
0
Robert
9/2/2013 9:24:15 PM
Quentin wrote:

> Do you know if XE and later know how to handle both Unicode
> and ANSI strings in TStringDynArray?

TStringDynArray only knows about the "string" data type, which is Ansi in 
D2007 and earlier, and Unicode in D2009 and later.  You can certainly declare 
"array of AnsiString" in your own code, but you can't make the RTL use it. 
 The RTL/VCL is almost exclusively Unicode nowadays (if you don't count Ansi-oriented 
functions that are still provided for backwards compatibility purposes).

--
Remy Lebeau (TeamB)
0
Remy
9/2/2013 9:28:49 PM
Robert wrote:

> Where can one find the code for TDirectory.GetFiles?

System.IOUtils.pas in the $(BDS)\source\rtl\common folder.

> When I look in System.IOUtils.pas, I just see a call to GetFiles().
> Where does that come from?

TDirectory.GetFiles() calls TDirectory.DoGetFiles(), which calls TDirectory.WalkThroughDirectory() 
with a callback function that adds each matching file to the Result array, 
resizing the array each time.

> In other words, how can one know:
> 
> "... it is resized/reallocated on every single file that is found."

By this code in TDirectory.DoGetFiles():

{code:delphi}
if CanAdd then
begin
  SetLength(ResultArray, Length(ResultArray) + 1);
  ResultArray[Length(ResultArray) - 1] := TPath.DoCombine(Path, FileInfo.Name, 
False);
end;
{code}

--
Remy Lebeau (TeamB)
0
Remy
9/2/2013 9:50:17 PM
Remy,

That's pretty much what I would have guessed.

Thanks!!! 


-- 

   Q 

09/02/2013 15:31:13

1.19.1.372  [Q'sBrokenToolBar] [Running on TQ]
0
Quentin
9/2/2013 10:32:40 PM
Remy Lebeau (TeamB) wrote:

> Peter wrote:
> 
> > The TDirectory.GetFiles function returns a dynamic array it has sized and
> > filled with the files to return internally.
> 
> An array that is sized quite inefficiently, I might add - it is resized/reallocated 
> on every single file that is found.  Embarcadero should know better than 
> to do that on (potentially) large lists.  I would think it would be better 
> to add the files to a TStringList first, and then allocate+fill the final 
> array only once from that.  And then maybe they should forget the array altogether 
> and just return the TStringList itself (or let the caller pass in a TStrings 
> to be filled).  Sometimes I worry about what Embarcadero designers are thinking 
> when they design public interfaces with inefficient implementations.

Or they could size the return list to some UWAG (unscientific wild-ass guess) size and track the file count, bumping the list occasionally, and then set the Length when they're done.
Actually, that's pretty much what the original TStringList did.

-- 
John
0
John
9/3/2013 3:01:30 AM
Robert,

> Actually, the for loop should be:
> 
>    for I := 0 to Length(ZipList) - 1 do
>      ListBox1.Items.Add(ZipList[I]);

as Remy said you can also use a for-in-loop or the old Low and High
functions.

{code: Delphi}
    for I := Low(ZipList) to High(ZipList) do
      ListBox1.Items.Add(ZipList[I]);
{code}

-- 
Roman Kassebaum
Embarcadero Technology Partner
Embarcadero MVP
Roman's Blog: http://blog.kassebaum.eu
0
Roman
9/3/2013 5:24:20 AM
Remy Lebeau (TeamB) wrote:

> Peter wrote:
> 
> > The TDirectory.GetFiles function returns a dynamic array it has sized and
> > filled with the files to return internally.
> 
> An array that is sized quite inefficiently, I might add - it is resized/reallocated 
> on every single file that is found.  Embarcadero should know better than 
> to do that on (potentially) large lists.  I would think it would be better 
> to add the files to a TStringList first, and then allocate+fill the final 
> array only once from that.  And then maybe they should forget the array altogether 
> and just return the TStringList itself (or let the caller pass in a TStrings 
> to be filled).  Sometimes I worry about what Embarcadero designers are thinking 
> when they design public interfaces with inefficient implementations.

Or they could size the return list to some UWAG (unscientific wild-ass guess) size and track the file count, bumping the list occasionally, and then set the Length when they're done.
Actually, that's pretty much what the original TStringList did.

-- 
John
0
John
9/3/2013 4:38:57 PM
John Treder wrote:

Dunno why Xananews pumped this again this morning.
-- 
Tredmill
0
John
9/3/2013 4:45:00 PM
Quentin Correll wrote:

> Peter,
> 
> >  There is no magic involved here. TStringDynArray is an array of
> >  string, and the code behaves exactly as if it were declared with
> >  ZipList: array of string. The TDirectory.GetFiles function returns
> > a  dynamic array it has sized and filled with the files to return
> >  internally.
> 
> Do you know if XE and later know how to handle both Unicode and ANSI
> strings in TStringDynArray?  (I'm to busy at the moment to play with
> it. <g>)

TStringDynArray is simply an array of string, i.e. of one single type.
That will handle the string type that is currently aliased to "string".
Other types will be converted, if this is implicitly possible.

-- 
Rudy Velthuis (TeamB)    http://www.teamb.com

"I'm all in favor of keeping dangerous weapons out of the hands 
 of fools. Let's start with typewriters."
-- Frank Lloyd Wright (1868-1959)
0
Rudy
9/4/2013 12:03:17 PM
Rudy,

| TStringDynArray is simply an array of string, i.e. of one single type.
| That will handle the string type that is currently aliased to
| "string".  Other types will be converted, if this is implicitly
| possible.

Thanks! 


-- 

   Q 

09/04/2013 10:44:47

1.19.1.372  [Q'sBrokenToolBar] [Running on TQ]
0
Quentin
9/4/2013 5:45:50 PM
Reply: