Is goto Still Considered Harmful?

J

John Bode

Is goto Still Considered Harmful?

There are times when they can be useful, such as backing out of
a deeply nested loop or complicated allocation/initialization
process. Having said that, I've used a goto exactly once in production
code, and later realized I didn't need to.

My problem has less to do with the goto itself and more with the
"come from" side of things. Take the following code snippet:

i = 5;
label: printf( "%d\n", i );

What value gets printed for i? *When* does it get printed? Until I
account for every instance of "goto label;" in the function, I can't
know that.

Gotos (or, more specifically, labeled statements) make code harder to
debug and review by inspection; that's my main issue with them.

My rules for using gotos:

1. Branch forward only;
2. Don't bypass the entry of a standard control structure;
3a. Don't use goto in place of a standard control structure;
3b. Don't use goto if break or continue will work just as well;
4. Don't overlap gotos;

Obviously, if you need to exit a nested loop, a break won't
be sufficient (it will only break out of the innermost loop).
 
S

Stefan Ram

John Bode said:
i = 5;
label: printf( "%d\n", i );
What value gets printed for i? *When* does it get printed? Until I
account for every instance of "goto label;" in the function, I can't
know that.

What about this: void f( int const i ){ printf( "%d\n", i ); }?

I can use the same wording now:

What value gets printed for i? *When* does it get
printed? Until I account for every instance of a
call of that function f, I can't know that.

Moreover, values in programming are /runtime/ values. That
means that in general they cannot be deduced by a static
code analysis anyways.

And does it always matter to know all this?
Obviously, if you need to exit a nested loop, a break won't
be sufficient (it will only break out of the innermost loop).

The whole thread suffers from talking vaguely about code
instead of looking at a fixed and complete and specific
code and a specification of what it is suppossed to do.

When talking vaguely in that matter everyone can say
everything and can never be proven false.
 
K

Ken Brody

Is goto Still Considered Harmful?

There are times when they can be useful, such as backing out of
a deeply nested loop or complicated allocation/initialization
process. Having said that, I've used a goto exactly once in production
code, and later realized I didn't need to. [...]
My rules for using gotos:

1. Branch forward only;
2. Don't bypass the entry of a standard control structure;
3a. Don't use goto in place of a standard control structure;
3b. Don't use goto if break or continue will work just as well;
4. Don't overlap gotos;

Obviously, if you need to exit a nested loop, a break won't
be sufficient (it will only break out of the innermost loop).

I use it to prevent multiple "return"s, without going through hoops.

For example:

int some_function()
{
int retcode = 0;
...
if ( something_fails() )
{
retcode = -1;
goto done;
}
... more code ...
if ( something_else_fails() )
{
retcode = -1;
goto done;
}
... more code ...
if ( another_something_else_fails() )
{
retcode = -1;
goto done;
}
... etc etc etc ...
done:
clean_up();
return(retcode);
}

Yes, the gotos can be avoided by putting everything inside of
"if(retcode>=0)" statements, but I personally find the "goto" to be cleaner
in these cases.

And, yes, I know that the parens on the "return" are unnecessary. Again,
it's just a matter of style.
 
S

Stefan Ram

Ken Brody said:
int some_function()
{
int retcode = 0;
...
if ( something_fails() )
{
retcode = -1;
goto done;
}
... more code [1] ...
if ( something_else_fails() )
{
retcode = -1;
goto done;
}
... more code [2] ...
if ( another_something_else_fails() )
{
retcode = -1;
goto done;
}
... etc etc etc ...
done:
clean_up();
return(retcode);
}

int f3(){ ... etc etc etc ... return 0; }
int f2(){ ... more code [2] ... return something_else_fails() ? -1 : f3(); }
int f1(){ ... more code [1] ... return something_else_fails() ? -1 : f2(); }
int f(){ int r = something_fails() ? -1; else f1(); clean_up(); return r; }
 
K

Kenny McCormack

There are times when they can be useful, such as backing out of
a deeply nested loop or complicated allocation/initialization
process. Having said that, I've used a goto exactly once in production
code, and later realized I didn't need to. ....
Gotos (or, more specifically, labeled statements) make code harder to
debug and review by inspection; that's my main issue with them.

My rules for using gotos:

1. Branch forward only;
2. Don't bypass the entry of a standard control structure;
3a. Don't use goto in place of a standard control structure;
3b. Don't use goto if break or continue will work just as well;
4. Don't overlap gotos;

Here's my real life example of where I think a goto would be a good thing.

In "sh" shell script, there is no goto. Therefore, to implement logic
like:

:tryagain
# stuff
# stuff
# stuff - try something that might (rarely) fail
# If it fails, display message and goto tryagain
# else continue on with life

you have to write:

while :;do
# stuff
# stuff
# stuff - try something that might (rarely) fail
# If it fails, goto tryagain
# If it fails, display message and "continue".
# continue on with life
break
done

I don't know about you, but I really dislike the second version.
 
B

BartC

John Bode said:
There are times when they can be useful, such as backing out of
a deeply nested loop or complicated allocation/initialization
process. Having said that, I've used a goto exactly once in production
code, and later realized I didn't need to.

My problem has less to do with the goto itself and more with the
"come from" side of things. Take the following code snippet:

i = 5;
label: printf( "%d\n", i );

What value gets printed for i? *When* does it get printed? Until I
account for every instance of "goto label;" in the function, I can't
know that.

That's not much different from:

void fn(int i) {
printf("%d\n",i);
}

What's the value of i here? And this can be called from anywhere in an
application, not just the current function.

With labels, I sometimes add a comment as to where the goto is likely to be
from.
 
B

BartC

Stefan Ram said:
Ken Brody said:
int some_function()
{
int retcode = 0;
...
if ( something_fails() )
{
retcode = -1;
goto done;
}
... more code [1] ...
if ( something_else_fails() )
{
retcode = -1;
goto done;
}
... more code [2] ...
if ( another_something_else_fails() )
{
retcode = -1;
goto done;
}
... etc etc etc ...
done:
clean_up();
return(retcode);
}

int f3(){ ... etc etc etc ... return 0; }
int f2(){ ... more code [2] ... return something_else_fails() ? -1 :
f3(); }
int f1(){ ... more code [1] ... return something_else_fails() ? -1 :
f2(); }
int f(){ int r = something_fails() ? -1; else f1(); clean_up(); return
r; }

Let's see, compared with just using a goto:

* The goto requires one label identifier to be created. Here you've added
three more function identifiers for no good reason (and in general, there
will be N identifiers need to be thought up).

* The single goto label identifier will be found towards the end of this
function. The function identifiers could conceivably be anywhere in the
application.

* You now have the huge headache of passing all the parameters and local
variables used by the main function, to all the sub-functions, and might
have to use pass-by-reference to allow those variables to be modified.

* If you already have several hundred functions in an application, they will
now be swamped, and the namespace polluted, by a thousand sub-functions,
with complex interfaces, that serve no useful purpose other than proving you
just could about cope without gotos if you had too.

* Your function might also now violate certain coding standards, eg:
"Rule 25
Functions should be no longer than 60 lines of text and define no more
than 6 parameters."
(http://lars-lab.jpl.nasa.gov/JPL_Coding_Standard_C.pdf).

The new main function might be smaller, but the contents of the original are
now disseminated throughout the rest of the program! At least, it will now
likely be incomprehensible.
 
M

Malcolm McLean

Gotos (or, more specifically, labeled statements) make code harder to
debug and review by inspection; that's my main issue with them.

My rules for using gotos:

1. Branch forward only;
2. Don't bypass the entry of a standard control structure;
3a. Don't use goto in place of a standard control structure;
3b. Don't use goto if break or continue will work just as well;
4. Don't overlap gotos;
Generally I use gotos only for jumping to an error_exit label. Which boils
down to memory allocation failure (for a pure function), or IO error for
an IO routine.
But there was an exception recently. A Newton_Raphson root finder. Almost
always you guess a root and it converges on a near answer. But just occasionally it can get stuck in an alternating cycle. So you need to
set the guess to a random other value, and restart it.
Whilst you could write this in non-goto control structures, you still
need a flag, and it obscures the fact that restarting is pretty unlikely to
happen. So I decided a backwards goto was better.
 
J

John Bode

What about this: void f( int const i ){ printf( "%d\n", i ); }?

The function f is now extremely easy to review and inspect; I know
that the value specified as the argument will be printed once per
call. There's a clearly defined entry and exit. No matter how the
function is called, I have a clear idea of how the function itself
behaves by simple inspection.
I can use the same wording now:

What value gets printed for i? *When* does it get
printed? Until I account for every instance of a
call of that function f, I can't know that.

You've missed whole the point of my example. By, like, a *lot*.
Moreover, values in programming are /runtime/ values. That
means that in general they cannot be deduced by a static
code analysis anyways.

And does it always matter to know all this?

When you're tasked with reverse engineering a monolithic, spaghettified
monstrosity to figure out how to rewrite it so that it's faster, more
stable, and more maintainable, *yes*.

I've been in that movie more than once. I've been given a pile of code
that sort-of-but-doesn't-really work with no documentation or requirements
and told to "figure out what it does and make it better". And every time I
see a labeled statement in those situations my heart sinks a bit, because
now the job is that much harder. IME there's a strong correlation between
the presence of a labeled statement and poor programming practice; functions
that are hundreds (sometimes thousands) of lines long that do twenty different
things, gotos branching hither and yon, inconsistent naming conventions, etc.

At least if I'm reviewing new code, I can kick it back to the author and say
"nope, try again, I'm not approving this crap." But if it's code where the
original author has long since skedaddled and the money won't let you just
rewrite the whole thing from the keel up like you should? Then yeah, this
matters.
The whole thread suffers from talking vaguely about code
instead of looking at a fixed and complete and specific
code and a specification of what it is suppossed to do.

The OP was as general as possible - are gotos considered harmful? Not,
are gotos considered harmful *in this situation*, or, are gotos considered
harmful *in this specific code*, but, are gotos considered harmful, *period*?

There's *no way* to answer a question like that except in generalities.
 
J

John Bode

That's not much different from:

void fn(int i) {
printf("%d\n",i);
}

What's the value of i here? And this can be called from anywhere in an
application, not just the current function.

Like Stephan, you've pretty much missed the point of my example, so let
me expand on it.

I'm tasked with tracing the flow of control within a single function to
determine where a particular bug occurs. This bug has to do with the
output of a particular variable (I expect it to be 5 at some point, but
it never is). So I start from the output statement:

i = 5;
label: printf("%d\n", i);

Okay. Assuming direct flow of control, I would expect the printf statement
to output 5 (after all, the variable is set in the statement immediately
before the printf call), but the presence of the label implies that the flow
of control *isn't* direct; there is *at least* one way to reach that print
statement without going through the "i = 5;" statement. So I look for any
occurrences of "goto label;" and find the following:

void foo()
{
int i = 0;
...
i = 5;
label:
printf("%d\n", i);
...
i = bar();
goto label;
...
}

Huh. So I find a line that branches back to the printf statement after
i is set to the result of bar(), but why am I not getting a 5 for output?

Expanding my search, I find the following:

void foo()
{
int i = 0;
...
goto bletch;
...
i = 5;
label:
printf("%d\n", i);
...
bletch:
...
i = bar();
goto label;
...
}

Yuck. So, it looks like that "i = 5;" statement was completely bypassed;
the code doesn't do what I expected it to do from a cursory inspection.

Yeah, sure, I can build a debug version and run it through gdb to figure out
how we get to that print statement. I can spend time stepping through a
function line by goddamned line looking for a stupid bug because it's not
like I have anything else to do.

Is that example completely contrived? I wish I could say it was. It was
inspired by real code I've had to review in the past (except that "foo" was
several *thousand* lines long and there were 12 or 13 more labels with
gotos branching all over the goddamned place).

Now, this was bad code without the presence of the gotos, but the
gotos made a bad situation impossible. The original author couldn't be
bothered to factor common code out into subroutines; he *simulated* them
by using gotos, but made such a hash of it that we couldn't fix one
execution path without breaking another.

My *point* was that as soon as you see a labeled statement, you can't make
any assumptions about the order in which that statement is executed relative
to the statement(s) immediately preceding it, and yes, THAT MATTERS. You
can't say anything about how the function works without tracing through every
possible permutation of gotos. The bigger the function, the harder that
task gets.
 
J

James Kuyper

The OP was as general as possible - are gotos considered harmful? Not,
are gotos considered harmful *in this situation*, or, are gotos considered
harmful *in this specific code*, but, are gotos considered harmful, *period*?

There's *no way* to answer a question like that except in generalities.

It is, however, entirely appropriate to bring up specific cases in an
attempt to prove that having a general rule is inappropriate.
 
S

Stefan Ram

John Bode said:
Is that example completely contrived? I wish I could say it was. It was
inspired by real code I've had to review in the past (except that "foo" was
several *thousand* lines long and there were 12 or 13 more labels with
gotos branching all over the goddamned place).

As others and I have said before: One might as well claim that in this
case, the problem is not the use of gotos, but the huge size of functions.

This is comp.lang.c; so, when someone asks about »goto«, we assume he
refers to »goto in C«. In C, we (except beginners) write short functions.

Of course, who here would deny that refactoring a huge old-style
monolithic program into a modern procedural and structured program is
a lot of (possibly unpleasant and difficult) work? But when one is doing
it for an appropriate pay, it might be fair.
 
K

Keith Thompson

As others and I have said before: One might as well claim that in this
case, the problem is not the use of gotos, but the huge size of functions.

This is comp.lang.c; so, when someone asks about »goto«, we assume he
refers to »goto in C«. In C, we (except beginners) write short functions.

I'm not aware of any particular connection between writing short
functions and the C langauge.

To put it another way, if we assume that writing short functions is A
Good Thing, I don't see how it's any more (or less) of a good thing in C
than in any other language.

Are there languages in which long functions are acceptable in your
opinion?
 
S

Stefan Ram

Keith Thompson said:
Are there languages in which long functions are acceptable in your
opinion?

When I have 32 Kibioctets for both code and data and a 1 MHz
processor and a compiler that does not optimize much to
implement a complex system, and writing a long function
instead of two shorter ones might save me some octets or
cycles, I might do it, when this kind of efficiency is important.
 
S

Stefan Ram

Keith Thompson said:
I'm not aware of any particular connection between writing short
functions and the C langauge.

On the World Wide Web I found a page »

17. Style
comp.lang.c FAQ list ·

«...»

Most observations or "rules" about programming style (

«...»

functions should fit on one page, etc.) usually work better as
guidelines than rules

«. So it says that »functions should fit on one page« in
»comp.lang.c« works/worked as a »guideline«. It should not
be followed slavishly as a rule, but as a guideline. While
there might be legitimate exception, this is the general idea.
The Web page mentions »comp.lang.c«, I wrote »we« above.

We here in comp.lang.c think that »functions should fit on
one page« must not be followed slavishly as a rule, but it
is a guideline.
 
J

James Kuyper

On 03/24/2014 07:32 PM, Stefan Ram wrote:
....
This is comp.lang.c; so, when someone asks about �goto�, we assume he
refers to �goto in C�. In C, we (except beginners) write short functions.

That has not been my experience. I tend to write short functions, but a
lot of the code I've seen written by other people involves very long
functions; thousands of lines in some cases. I don't think they were all
beginners (though I know that several of them were more familiar with
Fortran than with C).
 
M

Malcolm McLean

I'm not aware of any particular connection between writing short
functions and the C langauge.
In C, a function call is cheap. Then in decent C, you have to declare variables
in block scope, which means that if the function gets long, the local variable
list gets long, which acts as a kind of psychological warning that the function
is getting out of control. (A lot of people also use the convention that
variables must always be declared in function scope).
Then trivial standard library operations like strcpy() are also implemented
as functions, which encourages a "let's write short trivial functions"
mentality.
Then whilst it is possible to typedef basic types, it's not a very
sophisticated system. Most C programs use mainly basic types and pointers to
basic types. So this means functions tend to be reusable. What I mean is
that typically a C programmer will write circle_area(double r) rather than
myCircleType::getArea(). The first function can be cut and pasted into any
code that uses circles, the second function isn't reusable unless you pull
in the entire myCircleType suite, which you are unlikely to want to do.

Finally, you've got the static keyword, which puts functions into file scope.
So the C programmer can put in a strdup(), for example, without worrying about
exporting symbols. OK a bad example because strdup is in reserved namespace,
but the namespace was reserved because of precisely that problem.
 
K

Keith Thompson

On the World Wide Web I found a page »

17. Style
comp.lang.c FAQ list ·

«...»

Most observations or "rules" about programming style (

«...»

functions should fit on one page, etc.) usually work better as
guidelines than rules

«. So it says that »functions should fit on one page« in
»comp.lang.c« works/worked as a »guideline«. It should not
be followed slavishly as a rule, but as a guideline. While
there might be legitimate exception, this is the general idea.
The Web page mentions »comp.lang.c«, I wrote »we« above.

We here in comp.lang.c think that »functions should fit on
one page« must not be followed slavishly as a rule, but it
is a guideline.

You appear to be quoting http://www.c-faq.com/; it would have been good
to mention that. It's hard to tell just what you're quoting.

But there's nothing C-specific about the advice you cite; it just
happens to be from a site that deals with C. Would it not be equally
applicable to any other language?

Quoting another followup:
When I have 32 Kibioctets for both code and data and a 1 MHz
processor and a compiler that does not optimize much to
implement a complex system, and writing a long function
instead of two shorter ones might save me some octets or
cycles, I might do it, when this kind of efficiency is important.

That has nothing to do with the language. In fact, such a small system
is quite likely to be programmed in C.

And I don't feel as strongly as you do that functions should be short.
Each function should do one thing -- but sometimes that one thing is
very complex. *Arbitrarily* breaking a function down into smaller parts
is not always an improvement.
 
S

Stefan Ram

Keith Thompson said:
But there's nothing C-specific about the advice you cite; it just
happens to be from a site that deals with C. Would it not be equally
applicable to any other language?

I take the context of the newsgroup I am writing in into
account.

This is comp.lang.c, so I( try to) talk mostly about C.

Java has no functions (it has »methods«). It has other
compositional units, like classes, and a C functions
sometimes maps to a Java class, not a Java method:

C: int counter(){ static int value; return value++; }
Java: class Counter{ static int value; int value(){ return value++; }}

So, it is not obvious what constitues a »function« in
another language.

In classical BASIC, a function definition can be at most one
line long:

DEF FNA( x )= ...

. In classical FORTRAN, IIRC Functions can be defined, but
they are not »functions« in the sense of C (not recursive).

In 6502 assembler code, there are no functions.

Even in a language with recursive functions, like Pascal,
LISP or Haskell, the word »function« might have a different
meaning than in C. (Some C functions are procedures in
Pascal, while some C functions are split to many type
specific definitions in Haskell, LISPs uses lambda
expressions which are like function literals absent from C.)
And I don't feel as strongly as you do that functions should be short.
Each function should do one thing -- but sometimes that one thing is
very complex. *Arbitrarily* breaking a function down into smaller parts
is not always an improvement.

Yes, but often it can be broken non-arbitrarily.
 
J

James Kuyper

I take the context of the newsgroup I am writing in into
account.

This is comp.lang.c, so I( try to) talk mostly about C.

Yes, but you've been asked to defend an assertion that there is a
C-specific preference for short functions. The reasons supporting such a
claim must be C-specific; the reasons given on that page are not, even
though the site itself is C-specific.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top