Name McChange Name McChange - 4 months ago 14
Pascal Question

Pascal: Got to subtract 1 from length to get right size

I've got a piece of code like this:

for I := 0 to Self.EventQueue.Count do
Dispose(Self.EventQueue[I]);


It bugs out when the Count is 0, because it tries to
Dispose
a nonexisting element. When I change it to

for I := 0 to Self.EventQueue.Count-1 do
Dispose(Self.EventQueue[I]);


All works fine. Is there any elegant way to get around this or is this common practice?

Answer

This is absolutely normal behavior, and is documented in the help for every list and container class in Delphi/FreePascal. The reason is pretty clear - if you have three items in the list, and the first item is at index 0, then you have items 0, 1, 2 but a Count of 3, right?

for i := 0 to StringList.Count - 1 do  // TStringList

for i := 0 to List.Count - 1 do        // TList

for i := 0 to StringGrid1.ColCount do  // TStringGrid

The alternative isn't as clear (and to me is worse to type):

for i := 0 to Pred(StringList.Count) do  

Dynamic arrays start at index 0 as well.

var
  IntArray: array of Integer;
  i: Integer;
begin
  SetLength(IntArray, 10);
  for i := Low(IntArray) to High(IntArray) do  // loop is 0..9
    //
end;

The only things that aren't 0 based in FPC/Delphi are string types, which start at 1, and non-dynamic arrays (ones that are declared with a fixed size in code), which can start at almost any index you want. For instance, this is perfectly legal:

var
  IntArray: array[-10..10] of Integer;
  i: Integer;
begin
  for i := Low(IntArray) to High(IntArray) do // loop is -10..10
    //
end;

Just as an aside, any time you do anything in your loop that will reduce the number of items in your list, you should iterate backwards:

for i := List.Count - 1 downto 0 do

Otherwise, you'll iterate beyond the end of the list, because the Count is only evaluated at the time the loop starts.