Page 2 of 3

Re: goto instruction

Posted: Thu Mar 10, 2011 5:32 am
by Tosi
I have but one C goto in my entire OS project, in an implementation of vsprintf, and I could easily remove it. It's just not needed for a higher-level language like C that provides constructs like if statements.

While I don't think that goto is inherently evil, I feel it is not necessary for 99% of code, and it really is detrimental to readability. Even in systems-level programming, I believe the two most important considerations should be robustness and maintainability, and that performance is secondary to those. Not to mention modern C compilers are really good at optimization (inline functions)and the speed boost gained by replacing an if statement here or there with a goto would be minimal.

Re: goto instruction

Posted: Thu Mar 10, 2011 8:51 am
by Fanael
Solar wrote:
turdus wrote:Your code (which is good and readable solution) has a disadvantage that you introduced plus stack handling...
inline...
It doesn't matter that much, the compiler can, and it often does:
  • inline a function which wasn't marked as inline
  • not inline a function marked as inline
So, by factoring out the code into a function, one may have introduced stack handling, one may not, it depends on her compiler, as she left the decision to it. The good thing, however, is that the compiler's choice is likely to be better than her own. So she wins twice: her code is more readable (not because she avoided goto, but rather since now she has a single routine which does one and only one thing and does it well [1]) and it may be faster, smaller, or whatever she told the compiler to optimize for. And even if the generated code isn't any better, it's unlikely to be worse.
Solar wrote:
...and two more branches.
Nope, I copied your example exactly, including the check on expr1 and expr2.
I think he meant call and ret, bl and bx, etc.


By the way: if goto is soooooo evil, then what about continuations?


[1] Did you all notice? It's not about goto at all. In well-written code there are no gotos, just because they're not necessary.

Re: goto instruction

Posted: Thu Mar 10, 2011 9:24 am
by Solar
Fanael wrote:By the way: if goto is soooooo evil, then what about continuations?
Both "break" (outside a switch-case construct) and "continue" break the control flow. (Again, drawing a NSD makes this obvious.)

The "continue" keyword usually points to a suboptimal algorithm, and can often be avoided with some simple code rearrangement. However, sometimes it does make things much easier. (I used "continue" in two places in PDCLib, one of them the malloc() test driver, the other being strtok() which is pretty convoluted anyway.)

The "break" keyword, on the other hand, is quite similar to goto in terms of evilness: You introduced a loop, including a condition on which the loop may be terminated - and then introduced another terminating condition within the loop. Someone quickly browsing your code might have missed the "break" and continue working on false assumptions. (Needless to say, there are no non-case "break"s in PDCLib source, just like there are no "goto"s.)

Gee, I just found this in the Pro-POS Style Guide of old:
3.4 goto, continue, break

Do not use goto, period.

Do not use break anywhere else than to end a case block, period.

Try to avoid continue where possible.

These three basically break structured programming. You will see if you ever attempt to express a control flow containing any of those in a Nassi-Shneiderman diagram. continue is a little less condemnable since it at least honors the loop condition.

Instead, consider:
  • redefining your algorithm so that the goto / break / continue becomes unnecessary;
  • using if-else;
  • putting the code in a subroutine, "breaking" from it with a return.
If using option 3), do not forget to check the return value.

Re: goto instruction

Posted: Thu Mar 10, 2011 9:34 am
by Fanael
But neither break nor continue have anything to do with continuations.

Re: goto instruction

Posted: Thu Mar 10, 2011 9:50 am
by Combuster
And continuations have nothing to do with goto.

Re: goto instruction

Posted: Thu Mar 10, 2011 10:13 am
by Solar
Huh?

If you are asking about setjump() and longjump(), begone afore I find my mighty foam cluebat. :twisted:

Re: goto instruction

Posted: Thu Mar 10, 2011 10:31 am
by Fanael
I'm asking about real continuations, not about that heinous imitation.
Combuster wrote:And continuations have nothing to do with goto.
Some people would not agree.

Re: goto instruction

Posted: Thu Mar 10, 2011 11:09 am
by Combuster
Continuations are references to (the remaining steps of) an algorithm. They are not control flow instructions, and therefore offtopic. More importantly, its not part of standard C, and the language doesn't sanely implement it either.

I would love to discuss it though - its much less religion-sensitive than "goto", but a moderator would have to split the thread first in that case.

Re: goto instruction

Posted: Thu Mar 10, 2011 12:00 pm
by Fanael
When I say "continuations", I mean "continuations and related things", which includes among other things call/cc, which is a control flow instruction, so it's only a slight off-topic at most. I agree a thread split would be a better idea than continuing here nevertheless.

Re: goto instruction

Posted: Thu Mar 10, 2011 1:18 pm
by sancho1980
I hope this example is suitable enough just to give you some food for though.
Instead of writing something like this:

Code: Select all

int func(int x)
{
  int result = x;

  if (x > 1) {
    if (x > 2) {
      if (x > 3) {
        if (x > 4) {
          if (x > 5) {
            if (x > 6) {
              if (x > 7) {
                if (x > 8) {
                  if (x > 9) {
                    result += x * 3 + 8;
                  }
                  result -= 3;
                }
                result += 5 * 9 * x;
              }
              result -= 9 * x;
            }
            result *= 3 * x;
          }
          result -= 5 * 6 * 7;
        }
        result -= 7;
      }
      result *= 2 * x;
    }
    result /= 3;
  }
  result *= 25;
  return result;
}
I'd always prefer this:

Code: Select all

int func(int x)
{
  int result = x;

  if (x < 10) {
    goto over_10;
  }
  result += x * 3 + 8;
over_10:
  if (x < 9) {
    goto over_9;
  }
  result -= 3;
over_9:
  if (x < 8) {
    goto over_8;
  }
  result += 5 * 9 * x;
over_8:
  if (x < 7) {
    goto over_7;
  }
  result -= 9 * x;
over_7:
  if (x < 6) {
    goto over_6;
  }
  result *= 3 * x;
over_6:
  if (x < 5) {
    goto over_5;
  }
  result -= 5 * 6 * 7;
over_5:
  if (x < 4) {
    goto over_4;
  }
  result -= 7;
over_4:
  if (x < 3) {
    goto over_3;
  }
  result *= 2 * x;
over_3:
  if (x < 2) {
    goto over_2;
  }
  result /= 3;
over_2:
  result *= 25;
  return result;
}

Re: goto instruction

Posted: Thu Mar 10, 2011 2:07 pm
by qw
WHAT!?

Re: goto instruction

Posted: Thu Mar 10, 2011 2:21 pm
by Brynet-Inc
This thread is boring.

Re: goto instruction

Posted: Thu Mar 10, 2011 2:24 pm
by bewing
gerryg400 wrote: nobody said that using goto was related to intelligence or badness ...
Most of the comments were about this particular example ...
"... and asked me whether I thought I was smarter than all these programmers out there who say goto is bad ..."

And the OP's examples were clearly never intended to be thought about literally -- it was only a generic thought experiment. Taking it literally is silly and completely misses the point!
Solar wrote: Martin Fowler wrote:
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
I think the quote is unwise. Any fool can write code that a computer can understand. It takes someone smart to make code that a computer can run efficiently. Any fool can also write code that a human can understand which runs inefficiently. It takes someone very very smart to write code that runs efficiently, and is readable by humans.
Solar wrote: do you really believe what you just said? That software today runs "so slow" because people no longer use 'goto'?

"These three basically break structured programming."
Anyone who can draw a Nassy-Shneiderman diagram knows what I mean.
Once again, you are taking things too literally. I was speaking in a more generic sense. It's the basic attitude that "I shouldn't code for what the computer does easily, I should code for what a human can read." That's not "good programming". That's the philosophical basis of fatware.

I claim that you are under the misimpression that C is a structured language. The fact that it contains 'goto' is proof enough that it is not. The fact that you can torture and abuse the language into behaving like a structured language does not make it one. Mistakenly believing that C is a structured language is the religious basis of these discussions.

Re: goto instruction

Posted: Thu Mar 10, 2011 2:36 pm
by FlashBurn
I have an example where I like to use goto (and I think I´ve seen it in Linux too) for not making too much failures.

Code: Select all

int foo() {
 step;
 if(something)
  goto error1;
 if(somethingOther)
  goto error1;
 if(somethingStupid)
 goto error2;
 step;
 return bar;
error2:
 step;
error1;
 step;
error0;
 return error;
}
This will lead to smaller code and you will make less failures, because you´ve forgotten to update all if-code-ways.

Re: goto instruction

Posted: Thu Mar 10, 2011 2:55 pm
by sancho1980
I'm not in favor of using goto to jump back and forth. I would never use it to jump backward. It's just for the purpose of skipping some parts of my code.
And I fervently claim it SHOULD always be used to leave a function early. I find it absolutely confusing to see functions with several returns all over the place, because whenever later you need to add cleanup code to your function, you have to look at all of these returns. In fact, the very first lines I write when coding a new functions are these (ALWAYS):

Code: Select all

int func(params...)
{
  int result = 0;
  //TODO
finish:
  return result;
}
So when I later add dynamically allocated variables to my function, all I have to do is something like this:

Code: Select all

int func(params...)
{
  int result = 0;
  mytype_t *x = NULL;
  //TODO
finish:
  if (x != NULL) {
    free(x);
  }
  return result;
}
Of course I have to be careful to assign NULL whenever I free my dynamic variables in the course of the function. I've taken this to the point where I find it the only acceptable situation where 2 statements may go on the same line - freeing and assigning NULL as in:

Code: Select all

int func(params...)
{
  int result = 0;
  mytype_t *x = NULL;
  //some code
  x = malloc(sizeof(mytype_t));
  //some more code
  free(x); x = NULL;
finish:
  if (x != NULL) {
    free(x);
  }
  return result;
}
But normally I don't free them before finish; instead I'd just leave x allocated until the end of the function; so "finish" is for me something like a garbage collector.
Only when I allocate my variable several times in the function, as in a loop, where I always make sure the "free()" is done RIGHT at the start of the loop:

Code: Select all

int func(params...)
{
  int result = 0;
  mytype_t *x = NULL;
  for(;;) {
    if (condition) {
      break;
    }
    if (x != NULL) {
      free(x); x = NULL;
    }
    x = malloc(sizeof(mytype_t));
    //do something with x
  }
finish:
  if (x != NULL) {
    free(x);
  }
  return result;
}
So now the benefit of using goto is that whatever happens, I can leave my function early and be sure I free all my variables:

Code: Select all

int func(params...)
{
  int result = 0;
  mytype_t *x = NULL;
  for(;;) {
    if (condition) {
      break;
    }
    if (x != NULL) {
      free(x); x = NULL;
    }
    x = malloc(sizeof(mytype_t));
    //do something with x
    if (some error detected) {
      //don't have to care about any allocated variables lying around, "finish" will take care of them
      goto finish;
    }
  }
finish:
  if (x != NULL) {
    free(x);
  }
  return result;
}
Just to counter some possible objections: It's also part of my convention to declare ALL variables at the start of the function and to initialise them with some default value (NULL, 0, etc).