Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2002-12

"for" loop questions

9 messages · 5 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Unknown author

Hi.

I have a question regarding for loops. If you execute the following code:

var
  L : TList;
  P : Pointer;
  I : Integer;
begin
  L := TList.Create;
  for I := 0 to L.Count - 1 do begin
    P := L[I];
  end;
end;

You get an exception telling you that the list index is out of bounds. L.Count returns "0", but the loop still executes (or at least, it tries to) once, which is incorrect. According to every language I've ever used, if L.Count is 0 the first time, the loop should never this a bug, or am I missing something? Of course the fix is to just put an "if L.Count > 0" before the "for" loop, but that's not the point - the point is I shouldn't have to. :)

Thanks.

Tim

Reply by email

Balogh, Karoly (Charlie/iNQ
to Unknown author

Hi,

Show quoted text 22 lines
I have a question regarding for loops. If you execute the following code: var L : TList; P : Pointer; I : Integer; begin L := TList.Create; for I := 0 to L.Count - 1 do begin P := L[I]; end; end; You get an exception telling you that the list index is out of bounds. L.Count returns "0", but the loop still executes (or at least, it tries to) once, which is incorrect. According to every language I've ever used, if L.Count is 0 the first time, the loop should never ? Think again... ;) Is this a bug, or am I missing something? Of course the fix is to just put an "if L.Count > 0" before the "for" loop, but that's not the point - the point is I shouldn't have to. :)

But you should. :) I don't know the type of L.Count, but i suppose it's unsigned. If L.Count is 0, and you try to count until L.Count-1, it it simply underflows at the -1 operation. And i will count to $FF, $FFFF or $FFFFFFF, or whatever, depending on the type of L.Count.

And this is why you get an exception.

Bye,

Charlie/iNQ .:%[ Cybernetic Hydraulic Android Responsible for ]%:. .:%[ Logical Infiltration and Exploration ]%:. .:%[ Member of TeamOS/2 Hungary ]%:. .:%[ Member of Hungarian Amiga User Group ]%:.

Reply by email

Unknown author
to Balogh, Karoly (Charlie/iNQ

From: "Balogh, Karoly (Charlie/iNQ)"
To:
Date sent: Sun, 08 Dec 2002 08:50:51 +0100
Subject: [fpc-pascal]Re: "for" loop questions Send reply to:

Hi,

Show quoted text 20 lines
> I have a question regarding for loops. If you execute the following > code: > > var > L : TList; > P : Pointer; > I : Integer; > begin > L := TList.Create; > > for I := 0 to L.Count - 1 do begin > P := L[I]; > end; > end; > > You get an exception telling you that the list index is out of > bounds. L.Count returns "0", but the loop still executes (or at > least, it tries to) once, which is incorrect. According to every > language I've ever used, if L.Count is 0 the first time, the loop > should never just tried it. If I just add "{$MODE DELPHI}" and "uses Classes;"

at the beginning, and change your final semicolon to period, I don't get any error message - the loop does _not_ execute. At least this is true with my 1.0.7 snapshot compiled November 20. So it works correctly.

Show quoted text 8 lines
> Is this a bug, or am I missing something? Of course the fix is to > just put an "if L.Count > 0" before the "for" loop, but that's not > the point - the point is I shouldn't have to. :) But you should. :) I don't know the type of L.Count, but i suppose it's unsigned. If L.Count is 0, and you try to count until L.Count-1, it it simply underflows at the -1 operation. And i will count to $FF, $FFFF or $FFFFFFF, or whatever, depending on the type of L.Count.

This isn't really correct either; this would only be true if you assign the result of subtraction to an unsigned variable; the calculation itself is done after conversion to a signed type.

And this is why you get an exception.

Not really, see above. The exception doesn't seem to come from the quoted code fragment (unless you've used some other compiler version and there's some bug in that particular version).

Tomas

Reply by email

Balogh, Karoly (Charlie/iNQ
to Unknown author

Hi,

Show quoted text 7 lines
> But you should. :) I don't know the type of L.Count, but i suppose > it's unsigned. If L.Count is 0, and you try to count until L.Count-1, > it it simply underflows at the -1 operation. And i will count to $FF, > $FFFF or $FFFFFFF, or whatever, depending on the type of L.Count. This isn't really correct either; this would only be true if you assign the result of subtraction to an unsigned variable; the calculation itself is done after conversion to a signed type.

Is this true in every compiler mode? :) I believe to you, but then i have no idea why i got some crashes with some cases very similar to this in my own sources... Since the loop doesn't get executed then. I simply added the checks, to avoid this case, because i thought it's trivial why it crashes...

> And this is why you get an exception. Not really, see above. The exception doesn't seem to come from the quoted code fragment (unless you've used some other compiler version and there's some bug in that particular version).

Whoops. ;) I got the point now. Anyway, i even didn't tested the source, because i was sure that causes the problem what i said.

Bye,

Charlie/iNQ .:%[ Cybernetic Hydraulic Android Responsible for ]%:. .:%[ Logical Infiltration and Exploration ]%:. .:%[ Member of TeamOS/2 Hungary ]%:. .:%[ Member of Hungarian Amiga User Group ]%:.

Reply by email

Unknown author
to Balogh, Karoly (Charlie/iNQ

From: "Balogh, Karoly (Charlie/iNQ)"
To:
Date sent: Sun, 08 Dec 2002 10:44:45 +0100
Subject: [fpc-pascal]Re: "for" loop questions Send reply to:

Show quoted text 14 lines
>> But you should. :) I don't know the type of L.Count, but i suppose >> it's unsigned. If L.Count is 0, and you try to count until >> L.Count-1, it it simply underflows at the -1 operation. And i will >> count to $FF, $FFFF or $FFFFFFF, or whatever, depending on the type >> of L.Count. > This isn't really correct either; this would only be true if you > assign the result of subtraction to an unsigned variable; the > calculation itself is done after conversion to a signed type. Is this true in every compiler mode? :) I believe to you, but then i have no idea why i got some crashes with some cases very similar to this in my own sources... Since the loop doesn't get executed then. I simply added the checks, to avoid this case, because i thought it's trivial why it crashes...

It depends how much similar those cases were... ;-) The following code _will_ crash, and it's still quite "similar" to the previous one:

{$MODE DELPHI}
uses
 Classes;
var
  L : TList;
  P : Pointer;
  I : Integer;
  W: word;
begin
  L := TList.Create;
  W := L.Count - 1;
  for I := 0 to W do begin
    P := L[I];
  end;
end.

Tomas

Reply by email

Matt Emson
to Balogh, Karoly (Charlie/iNQ: But you should. :) I don't know the type of L.Count, but i suppose it's unsigned. If…
Really? Think again... ;)

No, that *is* a bug.

Pascal for loops only run from smallest to largest (fact) when using 'to'. To run in reverse you *have* to use 'downto'.

Think about it... Integer is a signed value. It can hold negative values. If List.count = 0 then List.count -1 is equal to -1. For i := 0 to -1 do ; should never execute. For i := 0 downto -1 do ; *will* execute. This is very basic Pascal. It's certainly true in Delphi, and you can find countless occrrence within the VCL for this assumption.

> Is this a bug, or am I missing something? Of course the fix is to just > put an "if L.Count > 0" before the "for" loop, but that's not the

point -

Show quoted text 6 lines
> the point is I shouldn't have to. :) But you should. :) I don't know the type of L.Count, but i suppose it's unsigned. If L.Count is 0, and you try to count until L.Count-1, it it simply underflows at the -1 operation. And i will count to $FF, $FFFF or $FFFFFFF, or whatever, depending on the type of L.Count.

TList.Count returns an Integer in the VCL. If it doesn't in FPC, this is a bug. Even if it did, you should be able to upcast the value to in integer or longint.

You might thing that adding in the extra line is a safe guard, but then it actually slows the operation down. Writing optomized code is prefereable to unessesary code.

Matt

Reply by email

Matt Emson
to Unknown author: From: "Balogh, Karoly (Charlie/iNQ)" To: Date sent: Sun, 08 Dec 2002…

However, the code below is an example of what *not* to do ;-)

You are comparing signed and unsigned types, wore still you are first converting a signed type to an unsigned one before the comparison. I know you were simply giving an example that does crash, but doing what you have done in the code is pretty stupid anyway.

Matt

Show quoted text 15 lines
{$MODE DELPHI} uses Classes; var L : TList; P : Pointer; I : Integer; W: word; begin L := TList.Create; W := L.Count - 1; for I := 0 to W do begin P := L[I]; end; end.

Reply by email

Jonas Maebe
to Unknown author: From: "Balogh, Karoly (Charlie/iNQ)" To: Date sent: Sun, 08 Dec 2002…
This isn't really correct either; this would only be true if you assign the result of subtraction to an unsigned variable; the calculation itself is done after conversion to a signed type.

This isn't entirelly correct. The decision about what the resulttype of a calculation will be is decided in the following order (so once a rule matches, that rule is used and the other rules aren't tried anymore).

1) one operand is an int64 -> result is int54
2) one operand is a qword -> result is qword
3) one operand is a cardinal and the other is also a cardinal or a positive constant -> result is cardinal
4) one operand is cardinal and the other is a signed type or a negative constant
-> if range or overflow checking is on: convert everything to int64 -> if range and overflow checking is off: convert everything to cardinal
5) all other cases: convert everything to longint, result is longint

Note that the above is for 1.0.x of the compiler, I think 1.1 may have been made a little more "intelligent", so that e.g. a byte + byte is calculated using longint arithmetic, but that the result is still a byte.

So to get back to the original expression: cardinal - 1 = cardinal. However, since someone else said the result of that count method is a longint, the result will be a longint.

Finally, in response to the original poster: "for x := 0 to -1 do..." will
indeed not exexcute any iteration under FPC.

Jonas

Reply by email