17

Syntactically I see that they loop indefinitely until a break statement is reached, but are they compiled to the same thing? Is the for slightly faster because it doesn't have a condition to check? Aside from code readability, is there even a difference?

Corey Ogburn
  • 24,072
  • 31
  • 113
  • 188
  • See http://stackoverflow.com/questions/2288856/when-implementing-an-infinite-loop-is-there-a-difference-in-using-while1-vs-fo – Bertrand Marron Jul 27 '10 at 20:17
  • This is a good question. I find it interesting to see that the compiler optimizes the code down to a do while loop in the example code I tried this with. – Wil P Jul 27 '10 at 20:23
  • 1
    `for(;;)` is A) written by former C coders, and B) four characters shorter :) – hobbs Jul 29 '10 at 04:26

7 Answers7

36

Given this input:

private static void ForLoop()
{
    int n = 0;
    for (; ; )
    {
        Console.WriteLine(n++);
    }
}

private static void WhileLoop()
{
    int n = 0;
    while (true)
    {
        Console.WriteLine(n++);
    }
}

...you get this output:

.method private hidebysig static void  ForLoop() cil managed
{
  // Code size       14 (0xe)
  .maxstack  3
  .locals init ([0] int32 n)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  dup
  IL_0004:  ldc.i4.1
  IL_0005:  add
  IL_0006:  stloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000c:  br.s       IL_0002
} // end of method Program::ForLoop


.method private hidebysig static void  WhileLoop() cil managed
{
  // Code size       14 (0xe)
  .maxstack  3
  .locals init ([0] int32 n)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  dup
  IL_0004:  ldc.i4.1
  IL_0005:  add
  IL_0006:  stloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000c:  br.s       IL_0002
} // end of method Program::WhileLoop

Remarkably similar, I would say (identical, even).

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
21

In modern compilers, absolutely nothing.

Historically, however, for(;;) was implemented as a single jump, while while(true) also had a check for true.

I prefer while(true), since it makes it more clear what I am doing.

Mike Caron
  • 14,351
  • 4
  • 49
  • 77
2

I haven't examined the output code, but there should be no difference whatsoever. Any decent compiler will do simple enough loop optimization to see that the condition is a constant expression, and thus doesn't need checking every iteration.

If one is faster than the other, the C# compiler writers need something 'splained to them...

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
1

If I might, I'd suggest that you look at a somewhat different question. If you're using either of these often enough to care, you're probably structuring your code poorly. While there are things like embedded systems that really do run forever, loops in most normal code do not. Writing a loop that claims to run forever usually means you've hidden the exit condition for the loop somewhere inside, with some other control flow (e.g., if (whatever) break;) as the real exit from the loop.

That can and usually should be avoided. While there are situations where break statements make sense, they should generally be to handle unusual situations, not for writing a loop that says one thing but does another (i.e., says "run forever", but really does "run until condition is met").

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Me neither. When I was first learning to program in PL/I (I don't know if it even had a 'break' statement) the pattern was "read line of file; while it's not the end-marker, do the loop, which ends with reading the next line". That seems a violation of "don't repeat yourself". I would suggest that reading the line once, at the start of the loop, and exiting if it's an end-marker, is a better style. – supercat Jul 27 '10 at 20:25
  • @supercat: I agree, but it doesn't justify anything. You should be doing `while (file.getline())`, not `for (;;) if (!file.getline()) break;` – Jerry Coffin Jul 27 '10 at 20:31
  • @supercat: Yes, it's often cleanest to put the test into the *middle* of the loop and break out of it when it fails. – Steven Sudit Jul 27 '10 at 20:37
  • @Jerry: That's a plausible example, but hardly the only way things work. The sentinel could instead be a specific value, such as a "0". – Steven Sudit Jul 27 '10 at 20:38
  • @Steven: The value of the sentinel rarely (if ever) matters. Something like `for (;;) { x = next_value(); if (x == sentinel) break; process(x); }` can essentially always be converted to something like `while ((x=next_value()) != sentinel ) process(x);`. – Jerry Coffin Jul 27 '10 at 20:45
  • I once wrote an RFC 822 header parser that looped until it hit an empty line, but inside that loop it also had to peek ahead to see if the next line is a continuation and keep looping internally until it concatenated the whole thing. After all that, it had to check that the key/value pair was both syntactically and semantically valid, exiting if not. I suppose it's possible to obfuscate this so as to avoid a `while(true)` but why bother? This is a genuine case of multiple exit criteria that cannot be determined in a single location on top. – Steven Sudit Jul 27 '10 at 21:02
  • My conclusion from this is that avoidance of the infinite loop with internal breaks is a pointless ideal that only *hurts* code quality. – Steven Sudit Jul 27 '10 at 21:03
  • @Steven: I was very careful to say it should "usually" be avoided. Yes, insisting on avoiding it even when it's the only reasonable solution can reduce code quality -- but that's really pretty rare. The *usual* result is an improvement in code quality. – Jerry Coffin Jul 27 '10 at 21:26
  • I'm skeptical even about "usually". In your example, you show assignment and comparison being combined, but that's a bad practice. – Steven Sudit Jul 27 '10 at 22:08
  • 1
    @Jerry Coffin: That while() loop condition exceeds my threshold for how many side-effects an expression can have before it's considered ridiculous. Why bother with a loop body at all? Why not "while ((((x=next_value()) != sentinel) && (process(x),1));"? – supercat Jul 27 '10 at 23:12
  • @Supercat: Because just as the condition for exiting the loop *should* be in the loop condition, other code like `process(x);` that does *not* form part of the condition for exiting the loop should *not* be in the loop condition. "Think before you ask these things!" – Jerry Coffin Jul 28 '10 at 01:53
  • @Jerry Coffin: I was being a little sarcastic; my point was that I dislike writing conditional expressions with excessive side-effects, and I think your example is really pushing things. I don't mind "if" statements that directly check whether a function works, nor do I mind "do {} while(--i);" but side-effect-laden expressions at the top of a loop don't feel right to me. – supercat Jul 28 '10 at 15:27
0

They compile to the same thing. You can write a test app that implements both methods and then use ILDASM to confirm they IL is identical.

DarLom
  • 1,100
  • 2
  • 12
  • 30
0

A debug assembly complies down to while(true). Use reflector and you can see the results.

    static void Main(string[] args)
    {
        ExecuteWhile();

        ExecuteFor();
    }

    private static void ExecuteFor()
    {
        for (; ; )
        {
            Console.WriteLine("for");
            string val = Console.ReadLine();
            if (string.IsNullOrEmpty(val))
            {
                Console.WriteLine("Exit for.");
                break;
            }
        }
    }

    private static void ExecuteWhile()
    {
        while (true)
        {
            Console.WriteLine("while");
            string val = Console.ReadLine();
            if (string.IsNullOrEmpty(val))
            {
                Console.WriteLine("Exit while.");
                break;
            }
        }
    }

Inspecting the ExecuteFor method in Reflector.

private static void ExecuteFor()
{
    while (true)
    {
        Console.WriteLine("for");
        if (string.IsNullOrEmpty(Console.ReadLine()))
        {
            Console.WriteLine("Exit for.");
            return;
        }
    }
}

An optimized version of the same code produces different results for ExecuteFor

private static void ExecuteFor()
{
    do
    {
        Console.WriteLine("for");
    }
    while (!string.IsNullOrEmpty(Console.ReadLine()));
    Console.WriteLine("Exit for.");
}

For verbosity here is the optimized ExecuteWhile...

private static void ExecuteWhile()
{
    do
    {
        Console.WriteLine("while");
    }
    while (!string.IsNullOrEmpty(Console.ReadLine()));
    Console.WriteLine("Exit while.");
}
Wil P
  • 3,341
  • 1
  • 20
  • 20
0

As mentioned by others, with any modern compiler, it should make absolutely no difference.

I did a brief test on my computer, timing a couple of operations using one or the other, and they always took pretty much the same time. Of course, because of other processes running, these tests aren't 100% accurate, but if there is a difference in speed (there shouldn't be) then it is a microscopic one.

EdoDodo
  • 8,220
  • 3
  • 24
  • 30