Is goto Still Considered Harmful?

K

Kaz Kylheku


It never was.

Remember, Dijkstra wasn't an actual developer. He never worked on any software
project of consequence. (Oh, pardon me, the THE system.)

He also invented semaphores, which are the worst synchronization primitive
ever: and, ironicaly, can be considered to be "the goto of synchronization".

Just like, if we have gotos (in particular backward gotos), it is hard to
answer the question "by what sequence of computations did the instruction pointer
arrive at this location?" with semaphores it is hard to answer the question "by
what sequence of operations did this semaphore come to have this particular
counter value?" It is hard to prove the correctness of algorithms based
on semaphores.

Other than semaphores, Dijstra was known for his rant about goto, and a few
obvious algorithms, like graph traversals that any decent hacker can work out
as a matter of coding and debugging at the terminal.

GOTO is very useful in programming languages that support meta-programming.
Even if you don't write software which has visible uses of goto, your
meta-programming constructs can take advantage of it in their code generation.

All structured control flow constructs compile to test-and-branch type stuff.

A large block with a nest of gotos going every which way is the most efficient
way to compile state machines, or at least certain ones.

Here is the thing: both goto and destructive manipulation of data are symptoms
of imperative programming. They are flip-sides of the same thing.

If anything is harmful, it is destructive programming.

This is what semaphores and goto have in common: manipulation of state.

Even if you don't have in your imperative program, or any selection statement,
there is an implicit goto: the goto which makes the currently executed location
move to the next statement!

In a purely functional language, there is no such thing. The only reason you
ever have a construction like { S1; S2; return S3; } is that S1 and S2 have side
effects. Since are not capturing the value produced by S1 and S2 (if any!) the
only reason to evaluate them is that they modify something, do I/O, or both.

The action which makes S1 flow to S2 is a kind of built-in goto, and that's
part of the essence of what makes it imperative programming.

In imperative programming there is a tradeoff between goto and state variables.
By using goto, you can eliminate state variables, and conversely when you
eliminate goto, you may have to introduce state variables.

But state variables are just goto in disguise. For instance if in a state
machine you have

for (;;) {
switch (state) {
/* numerous cases */
}
}

and those cases make an assignment to the state variable, that assignment is
really a goto. When the switch bails out and the loop makes another round, the
value of state will arbitrarily guide the switch statement to a completely
different case from the previous iteration. This is just a circular way of
doing a "goto" from one case to the other, directly.

The state variable is a de-facto instruction pointer.

At the machine level, the instruction pointer is just a variable. In some
cases, just another general-purpose register that can be used like any other.
E.g. R31 or whatever, with the pseudonym PC in assembly-language,
and "JMP X" being an assembly language pseudo-op for "MOVE R31, X".

Clobbering a value in place is the "harmful" thing (but not really); goto is
just a scapegoat blamed by imbeciles.

If you think destructive manipulation of state is harmful, that pretty much
rules out C and anything like it, all together. You need a totally different
kind of language. It is not a pragmatic viewpoint, in any case.

If you embrace imperative programming, you're embracing goto.
 
J

Jos Bergervoet

It never was.

In your dreams, perhaps.

...
A large block with a nest of gotos going every which way is the most efficient
way to compile state machines, or at least certain ones.

So gotos are efficient in machine code, but the
discussion wasn't about efficient machine code.

...
If you embrace imperative programming, you're embracing goto.

No, you can still avoid them completely (although
there's no need to goto such an extreme effort..)
And of course keeping things functional will always
pay off!
 
B

BartC

Lynn McGuire said:

Yes it is frowned upon and it is bad practice. But I'd prefer that a
language still has it just in case (many don't; C does, otherwise I wouldn't
be here - I need it for auto-generated code).

But the code error in the article was about how code blocks are delimited:

if (a)
b;
c;
if (d) ...

Statements b and c are both indented and look at first glance as though they
belong to the preceding if-condition, but c (which happened to be a goto)
was always executed.

Insisting that all statement sequences have {...} braces, even when there is
only one statement, I think would have helped. (Also having statements
require a matching closing delimiter, but C uses {...} braces for
everything.)

As it is, it is common for a statement such as c to not have a } to close
it, so it's easier to miss the bug.
 
Q

Qolin

"BartC" wrote in message
Yes it is frowned upon and it is bad practice. But I'd prefer that a
language still has it just in case (many don't; C does, otherwise I
wouldn't
be here - I need it for auto-generated code).

But the code error in the article was about how code blocks are delimited:

if (a)
b;
c;
if (d) ...

Statements b and c are both indented and look at first glance as though
they
belong to the preceding if-condition, but c (which happened to be a goto)
was always executed.

Insisting that all statement sequences have {...} braces, even when there
is
only one statement, I think would have helped. (Also having statements
require a matching closing delimiter, but C uses {...} braces for
everything.)

On the contrary, IMO the problem is the way C code routinely puts the IF
statement, and subsequent
executed statement (goto in this case), on separate lines. Had the gotos all
been on the same lines as the IFs, the
error would have been much more obvious. Like this:

if ((err = ReadyHash (&SSLHashSHA1, &hashCtx )) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail;
goto fail;
if ((err = SSLHashSHA1.final (&hashCtx, &hashOut )) != 0) goto fail;

(and yes, even in C, _my_ code looks like Fortran).

Qolin at domain dot com
domain is qomputing
 
L

Lynn McGuire

"BartC" wrote in message

On the contrary, IMO the problem is the way C code routinely puts the IF statement, and subsequent
executed statement (goto in this case), on separate lines. Had the gotos all been on the same lines as the IFs, the
error would have been much more obvious. Like this:

if ((err = ReadyHash (&SSLHashSHA1, &hashCtx )) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail;
goto fail;
if ((err = SSLHashSHA1.final (&hashCtx, &hashOut )) != 0) goto fail;

(and yes, even in C, _my_ code looks like Fortran).

Qolin at domain dot com
domain is qomputing

Real programmers can write Fortran in any language.

Lynn
 
S

Stefan Ram

Lynn McGuire said:
Is goto Still Considered Harmful?

Is GOTO still considered harmful?

I'd say: When a function is small, it is not really so
confusing when there are some GOTOs that jump around inside
the small function. »break«, »return«, and »continue« are
GOTO-like, but sometimes are acceptable inside small functions.

Sometimes it can be faster to jump right out of a loop than
to set a boolean flag that will then terminate the loop (I
read a study about this once). When a search loop has found
a result, a return from within a loop seems to be ok.

That classic unmaintainable program would be - I think: A
large monolithic block of code with many global variables
and wild jumps from everywhere inside of the block to
everywhere else inside of it.

However, GOTO is not so helpful that I intentionally use it.
I would only deviate from structured programming when in dire
need to optimize.
 
K

Keith Thompson

Qolin said:
"BartC" wrote in message news:D[email protected]...

On the contrary, IMO the problem is the way C code routinely puts the IF
statement, and subsequent
executed statement (goto in this case), on separate lines. Had the gotos all
been on the same lines as the IFs, the
error would have been much more obvious. Like this:

if ((err = ReadyHash (&SSLHashSHA1, &hashCtx )) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail;
goto fail;
if ((err = SSLHashSHA1.final (&hashCtx, &hashOut )) != 0) goto fail;

(and yes, even in C, _my_ code looks like Fortran).

Very commonly, the code controlled by an if statement consists of
multiple statements that *can't* be written on a single line.
(Syntactically, an if statement always controls exactly one statement;
that can be a compound statement delimited by "{" and "}".)

If the controlled statement happens to be a single very short statement,
I don't mind putting the whole thing on one line, especially if it's one
of several parallel statements:

if (foo) this;
if (bar) that;
if (baz) the_other_thing;

But if it's long enough to need more than one line, I *always* use
braces:

if (some_condition) {
this;
that;
the_other_thing;
}

By stubbornly keeping to this habit, I avoid the kind of error we're
discussing. (It's a habit I picked up from Perl, which requires the
braces even if there's only one statement.)

(Feel free to put the opening "{" on its own line if you prefer.)

(This is cross-posted to comp.lang.fortran and comp.lang.c. I've
redirected followups to just comp.lang.c; if you want to reply to both,
you'll need to override that.)
 
S

Stefan Ram

Keith Thompson said:
Very commonly, the code controlled by an if statement consists of
multiple statements that *can't* be written on a single line.

In C, a whole program of many MBytes source code can be written
on a single line. The only exceptions are the preprocessor directives.
 
J

James Kuyper

In C, a whole program of many MBytes source code can be written
on a single line. The only exceptions are the preprocessor directives.


"Each instance of a backslash character (\) immediately followed by a
new-line character is deleted, splicing physical source lines to form
logical source lines." (5.1.1.2p2)

C implementations are not required to support logical source lines with
more than 4095 characters (5.2.4.1p1). Therefore, you should change
"many MBytes" to "4 KBytes".

Logical source lines are created during translation phase 2, so the only
things affecting the character count are the transformation from file
multi-byte characters to the source character set, and the conversion of
trigraphs to the corresponding single character. By making extensive use
of trigraphs and multi-byte characters, you might be able to stretch
this up to "many KBytes", but you can't get it up to "many MBytes".
 
A

Andrew Cooper

In your dreams, perhaps.

Only in your narrow view of the world then...
...

So gotos are efficient in machine code, but the
discussion wasn't about efficient machine code.


gotos are about all you have in most machine languages. And it is
always more efficient not to use them; branch predictors aren't perfect.
...

No, you can still avoid them completely (although
there's no need to goto such an extreme effort..)
And of course keeping things functional will always
pay off!

You could avoid them completely, at the expense of code clarity.

As far as I am concerned, you can pry goto from my cold dead hands. As
an operating system developer, there are *many* cases where goto is the
correct, and only sensible way, of structuring functions in such a way
as to be clear to read, and easy to reason regarding correctness.

~Andrew
 
K

Keith Thompson

In C, a whole program of many MBytes source code can be written
on a single line. The only exceptions are the preprocessor directives.

Amend that to:

.... that can't *reasonably* be written on a single line.
 
K

Keith Thompson

James Kuyper said:
"Each instance of a backslash character (\) immediately followed by a
new-line character is deleted, splicing physical source lines to form
logical source lines." (5.1.1.2p2)

C implementations are not required to support logical source lines with
more than 4095 characters (5.2.4.1p1). Therefore, you should change
"many MBytes" to "4 KBytes".

No, but they're allowed to do so, and many compilers (I think) impose no
restriction on line length other than available compile-time memory and
other resources. You can write a "conforming program" (but not a
"strictly conforming program") with, say, a few megabytes on one line.

Not that this is relevant to the original point, of course.

[...]
 
J

James Kuyper

No, but they're allowed to do so, and many compilers (I think) impose no
restriction on line length other than available compile-time memory and
other resources. You can write a "conforming program" (but not a
"strictly conforming program") with, say, a few megabytes on one line.

Not that this is relevant to the original point, of course.

The original point was a style issue that didn't interest me. The line
length was a matter of fact, which did interest me. At least, it
interested me more than the much more boring thing I should have been
doing. :)
 
K

Kaz Kylheku

gotos are about all you have in most machine languages. And it is
always more efficient not to use them; branch predictors aren't perfect.

Not always. There exist these things called unconditional branches,
which are 100% taken. Wink!
 
M

Melzzzzz

Only in your narrow view of the world then...



gotos are about all you have in most machine languages. And it is
always more efficient not to use them; branch predictors aren't
perfect.

Well they are not perfect but eg x86 have simple rule.
Forward branch is predicted not to be taken, while backward
branch is predicted to be taken.
 
J

Jens Thoms Toerring

In comp.lang.c Lynn McGuire said:

First of all, this actually's no issue restricted to a GOTO.
Everytime you do sometking like

if ( some_condition )
do_A( );
do_B( );

there's an obvious problem (at least if this has been written
under the assumption that do_B() would only be invoked in case
that 'some_condition' is true) - how bad the results are depends
on what the do_B() call does.

In some cases it might help to religiously enclose IF blocks in
curly braces to make such problems stand out more. E.g.

if ( some_condition ) {
do_A( );
}
do_B( );

may be more visible (but that always requires that someone is
actually looking at this code and starts wondering what's going
on).

The "Pythonistats" had their day, claiming that it never would
have happened to them due to to the relevance of white-space in
Python. I'm not convinced, I think it could have happened even
more easily in Python (given they had a GOTO statement) since
an editor can't even know when to indent.

With a reasonable editor (or a indentation tool) this flaw
could easily have been exposed to anyone taking jist a super-
ficial glance, i.e., if some rather easy to understand code
like this (after proper indentation)

if ( some_condition )
goto fail;
goto fail;
if ( some_other_condition )
goto fail;

doesn't raise an alarm bell what ever will? And the whole layout
of the code isn't very convincing:

if ((err = SSLFreeBuffer(&hashCtx)) != 0)
goto fail;
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx &clientRandom)) != 0)
goto fail;
goto fail;

Why not make it more easy to read like e.g.

if ( ( err = SSLFreeBuffer( &hashCtx ) )
|| ( err = ReadyHash( &SSLHashSHA1, &hashCtx ) )
|| ( err = SSLHashSHA1.update( &hashCtx &clientRandom ) ) )
goto fail;

(I'd also put a comment in front of that, trying to expalin
what that's supposed to check.)

The only thing I can see here is another example of "don't
repeat yourself" - if you have to write "goto fail;" many
times in a row something is fishy, someone just mechani-
cally copy-and-past-ed without giving it a thought...

I guess the only way to avoid such things is to indent code cor-
rectly (using all tools available) and looking out for anything
that doesn't look right on first sight.

And when it comes to Dykstra's "Goto considered harmful" this
seems to be really far-fetched. The paper is from 1968 (nearly
50 yeares ago) and times and programming practices were quite
different back then (quite before my time;-). While I'd comple-
tely agree that something like (and I guess that's something
akin to what Dykstra actually was ranting about)

GOTO i 237, 384, 293, 204, 381, 493, 1002, 536, 785, 662

- as could and would be done in FORTRAN (I've seen code like
this and was unlucky enough to have to try to unravel it) - is
definitely "harmful", but that's nothing anybody in a sane
state of mind would do in a modern computer language (unless
for an obfuscation contest). But that bears no relationship to
some simple and straightforward GOTOs in otherwise sane code.

Regards, Jens
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top