3

There are differences in execution between the 2 following C# snippets?

do
{
    Console.WriteLine(x.ToString());
    ++x;
} 
while (x < 7);

and

label:
{
    Console.WriteLine(x.ToString());
    ++x;
}
if (x < 7) goto label;

I am trying to figure out why is goto so bad. Thanks.

EDIT: If I add brackets the snippets are pretty similar.

EDIT2: In Visual Studio I've clicked Go to Disassembly and I get the following code:

            do
            {
00000037  nop 
                Console.WriteLine(x.ToString());
00000038  lea         ecx,[ebp-40h] 
0000003b  call        63129C98 
00000040  mov         dword ptr [ebp-48h],eax 
00000043  mov         ecx,dword ptr [ebp-48h] 
00000046  call        63148168 
0000004b  nop 
                ++x;
0000004c  inc         dword ptr [ebp-40h] 
            } 
0000004f  nop 
            while (x < 7);
00000050  cmp         dword ptr [ebp-40h],7 
00000054  setl        al 
00000057  movzx       eax,al 
0000005a  mov         dword ptr [ebp-44h],eax 
0000005d  cmp         dword ptr [ebp-44h],0 
00000061  jne         00000037 

and

            label:
            {
                Console.WriteLine(x.ToString());
00000069  lea         ecx,[ebp-40h] 
0000006c  call        63129C98 
00000071  mov         dword ptr [ebp-4Ch],eax 
00000074  mov         ecx,dword ptr [ebp-4Ch] 
00000077  call        63148168 
0000007c  nop 
                ++x;
0000007d  inc         dword ptr [ebp-40h] 
            }
00000080  nop 
            if (x < 7) goto label;
00000081  cmp         dword ptr [ebp-40h],7 
00000085  setge       al 
00000088  movzx       eax,al 
0000008b  mov         dword ptr [ebp-44h],eax 
0000008e  cmp         dword ptr [ebp-44h],0 
00000092  jne         00000097 
00000094  nop 
00000095  jmp         00000068

The difference is unconditional jump.

Adrian Stanculescu
  • 1,040
  • 2
  • 13
  • 21
  • 3
    goto creates spaghetti code. There is only a few cases where using goto is accepted such as jumpng out of multiple nested loops and case jumping in switch statements. - https://en.wikipedia.org/wiki/Spaghetti_code – Bauss Jul 28 '15 at 07:24
  • 2
    There are no differences in the execution of the two code segments, but when you need to write more complex code than that, code that uses `goto`'s becomes hard to maintain. – Candide Jul 28 '15 at 07:25
  • 1
    Related: http://stackoverflow.com/q/6545720/447156 and http://stackoverflow.com/q/2542289/447156 and http://stackoverflow.com/q/11906056/447156 – Soner Gönül Jul 28 '15 at 07:25
  • The bigger your methods get the worse it is to find the label. you also don'T have an indention or brackets which should what part of the code you want ot iterate through – Boas Enkler Jul 28 '15 at 07:25
  • 4
    Yep, the only "problem" with the bottom one is that future maintainers, or code reviewers, or your boss, are going to be left wondering "don't they know about `while` and other standard language constructs?" - "What other gems am I going to find in their code?" – Damien_The_Unbeliever Jul 28 '15 at 07:26
  • Check this - http://stackoverflow.com/a/11906082/2020893 – Karthik AMR Jul 28 '15 at 07:26
  • @Damien_The_Unbeliever: even more probably, **germs** not gems :) – quetzalcoatl Jul 28 '15 at 07:30
  • possible duplicate of [GOTO still considered harmful?](http://stackoverflow.com/questions/46586/goto-still-considered-harmful) – phuclv Jul 28 '15 at 07:32
  • it's not always bad but in general it's worse than other constructs, as it doesn't create good structures/blocks – phuclv Jul 28 '15 at 07:33
  • @Damien_The_Unbeliever: OK. So the main problem is the legacy code? – Adrian Stanculescu Jul 28 '15 at 07:34
  • @AdrianStanculescu The main problem of all software engineering is legacy code :) Be kind to yourself and your heirs, and make your code as maintainable as reasonably possible. The problem isn't that `goto` is the manifestation of everything evil. It's that it makes the code just a tiny bit easier to break, a tiny bit harder to understand. It's the same as using `Where` instead of a custom `foreach` for filtering collections - sure, it still works. But does it make sense? – Luaan Jul 28 '15 at 07:36
  • For better understanding I can use brackets inside label: – Adrian Stanculescu Jul 28 '15 at 07:38
  • Yes, but then why use `goto` in the first place? If you already have a structure like this, you can use a structured loop and be done with it. This *enforces* the use of the block, rather than leaving it optional. The only reasons to use `goto` are exactly when you *can't* put the "loop" in brackets. – Luaan Jul 28 '15 at 07:39
  • 1
    Obviously the problem can't be seen in a snippet. Think of large project and gotos jumping back and forth over pages.. – TaW Jul 28 '15 at 07:40
  • And think of copying code. What if you accidentally copy a piece of code that uses a `goto` that jumps to some label you didn't copy... but by chance, the method where you use this snippet already has a label with the same name. Hilarity ensues (not). Structured cycles are inherently *structured* and *local*. `goto` isn't either. Oh, and note how your `goto` snippet is actually *longer* than the `do`-`while`. What's the point of using a way that's harder to maintain, longer and so easy to misuse? – Luaan Jul 28 '15 at 07:42
  • 1
    Using `goto` *can* result in spaghetti code but it's by no means a certainty. One should understand rules rather than parrot them blindly: http://powerfield-software.com/?p=236 – paxdiablo Jul 28 '15 at 07:42
  • @paxdiablo: i'm trying to understand not take as it is. +1 – Adrian Stanculescu Jul 28 '15 at 07:59
  • 1
    Adrian, that comment wasn't directed at you specifically, rather it was to temper what I thought others were saying, that gotos always cause spaghetti code. In fact, they don't always do so, often they can _aid_ readability. – paxdiablo Jul 28 '15 at 08:03
  • Dijkstra knows best: [Go To Statement Considered Harmful](http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html) – Markus W Mahlberg Jul 28 '15 at 12:15
  • @Markus W Mahlberg What about Structured Programming with goto Statements by Donald Knuth? – Adrian Stanculescu Jul 28 '15 at 13:46

2 Answers2

9

No, I even think a while is implemented like that in the back.

The thing bad in using goto is that it encourages going back and forth in your code (also known with the term 'spaghetti code': it is a mess). It makes your code extremely hard to read, debug and analyse and it introduces bugs since you can't really understand what is going on.

The nice thing with while is, that you can understand it, and the compiler can understand it, so it can give you nice warnings.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • True, but in some cases `while` loops are very artificial and the only purpose is to avoid `goto`. – i486 Jul 28 '15 at 07:27
  • 1
    It's not like there is a way to implement it differently, really. At some point, you have to do the jump (excepting a few special cases that have their own instructions like `repnz`). Another benefit to a `while` is that you have an explicit block that repeats itself - it's easy to see, it isolates the variables local to the block, all in all, it shields you from many small mistakes you can make. And of course, it's more resistant to code modifications - it's very easy to break `goto` code accidentally, especially now when source control is prevalent (automatic merge is a pain). – Luaan Jul 28 '15 at 07:35
  • Indeed @Luaan. You should prevent to use `goto` and only use if you really really need to (I have never seen situations, but okay). – Patrick Hofman Jul 28 '15 at 07:36
2

Lets take a look at the IL:

.method private hidebysig 
    instance void While () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 31 (0x1f)
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] bool CS$4$0000
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    // loop start (head: IL_0003)
        IL_0003: nop
        IL_0004: ldloca.s x
        IL_0006: call instance string [mscorlib]System.Int32::ToString()
        IL_000b: call void [mscorlib]System.Console::WriteLine(string)
        IL_0010: nop
        IL_0011: ldloc.0
        IL_0012: ldc.i4.1
        IL_0013: add
        IL_0014: stloc.0
        IL_0015: nop
        IL_0016: ldloc.0
        IL_0017: ldc.i4.7
        IL_0018: clt
        IL_001a: stloc.1
        IL_001b: ldloc.1
        IL_001c: brtrue.s IL_0003
    // end loop
    IL_001e: ret
} // end of method Program::While

.method private hidebysig 
    instance void Goto () cil managed 
{
    // Method begins at RVA 0x207c
    // Code size 34 (0x22)
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] bool CS$4$0000
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    // loop start (head: IL_0003)
        IL_0003: ldloca.s x
        IL_0005: call instance string [mscorlib]System.Int32::ToString()
        IL_000a: call void [mscorlib]System.Console::WriteLine(string)
        IL_000f: nop
        IL_0010: ldloc.0
        IL_0011: ldc.i4.1
        IL_0012: add
        IL_0013: stloc.0
        IL_0014: ldloc.0
        IL_0015: ldc.i4.7
        IL_0016: clt
        IL_0018: ldc.i4.0
        IL_0019: ceq
        IL_001b: stloc.1
        IL_001c: ldloc.1
        IL_001d: brtrue.s IL_0021

        IL_001f: br.s IL_0003
    // end loop

    IL_0021: ret
} // end of method Program::Goto

ILSpy event marks the goto as a loop. When decompiling it to C#, it even shows both loops as a do while.

But there is a different! The while loop has a scope:

        int x = 0;
        do
        {
            string z = "TEST";
            Console.WriteLine(x.ToString());
            ++x;
        }
        while (x < 7);
        Console.WriteLine(z); // invalid!

but this is valid:

        int x = 0;
    label:
        string z = "TEST";
        Console.WriteLine(x.ToString());
        ++x;
        if (x < 7) goto label;
        Console.WriteLine(z);

Should you use it? No! See Patricks answer.

Community
  • 1
  • 1
Alex H
  • 1,424
  • 1
  • 11
  • 25