regarding "goto" in C

R

Richard Tobin

C has perfectly good looping constructs.

C has perfectly good recursion, but not all compilers implement it
well enough for some purposes.

I would usually rather overcome the deficiencies of compilers by using
a goto than by changing the algorithm.

-- Richard
 
E

Emmanuel Delahaye

M.B a écrit :
We have an option of using "goto" in C language, but most testbooks
(even K&R) advice against use of it.

A good advice, specially targetted to beginners.
My personal experience was that goto sometimes makes program some more
cleaner and easy to understand and also quite useful (in error handling
cases).

True enough.
So why goto is outlawed from civilized c programmers community.

I think goto is for experienced programmers who know the limits and
dangers induced by the use of this very sharp tool...
is there any technical inefficiency in that.

No. It's essentially a design issue. Noone like spaghetti code.
 
P

pemo

C has perfectly good recursion, but not all compilers implement it
well enough for some purposes.

'but not all compilers implement it well enough for some purposes'

Please give an explanation

[I'm freaked out, and cannot sleep now]
 
E

Emmanuel Delahaye

Nils O. Selåsdal a écrit :
"Structured Programming with go to Statements"

This was my favorite game when I was programming in BASIC (the true one,
with "10 GOTO hell" etc.) and Assembly language... It drove by teacher
and supervisor crazy !
 
M

Malcolm

Keith Thompson said:
I can think of 3 cases where a goto is justified in C: an explicit
finite state machine, where a goto actually models something in the
problem domain (this could also be implemented as a switch statement
in a loop with an explicit state variable); error handling (due to the
lack of a decent exception handling mechanism); and breaking out of a
nested loop (due to C's regrettable lack of multi-level break).
I'd add a fourth - occasionally scratch code to examine program flow might
need a goto
(You can implement the debug control structure with loops, but that might
entail messy brackets that obscure the real non-debug control structure.)
 
R

Richard Tobin

'but not all compilers implement it well enough for some purposes'

Please give an explanation

Consider a function of the form

some_type foo(some args)
{
...
if(some condition)
return foo(some other args);
...
}

(obviously the recursive call has to be in a conditional). The
compiler does not need to create a new stack frame for the recursive
call, since it will never return to the current frame (except to
return immediately). This is called "tail-recursion optimization",
and if you can rely on it then you can use recursion of this sort even
in cases where the depth of recursion would be very deep, and might
otherwise run out of memory.

Some C compilers do this optimization in some cases (gcc for example)
but it is not guaranteed by the standard. Some other languages
(notably Scheme) do guarantee it.

-- Richard
 
C

Christian Bau

"Red Cent said:
Two things I think need pointed out here.

1) As mentioned earlier in this thread, goto does NOT necessarily lead
to poor programming. If used with care, goto maybe a good choice, but
there is always an alternative. Whether it's a function call, a case
switch. All of these can be good alternatives BUT they can be abused
as well.

All these anti-goto tirades come from a time in ancient history, where
widespread programming language had nothing but goto to implement simple
control flow. For example, in Fortran 66 there was not even a simple if
/ else / endif available. The forced use of goto because alternatives
were not available did certainly make programs less readable.

If anyone is studying C today, it is hard to imagine why they would use
goto in a substantial number of places, unless there is an especially
warped mind in place. If/else, loops, break and continue are just so
much easier to use in most cases.
 
P

Peter Nilsson

Marco said:
several coding standard/style books

"Enough Rope to Shoot Yourself In the Foot"
" The Elements of C Programming Style"

suggest that a forward goto from nested loops is perfectly acceptable

they have examples similar to

while( condtion1 )
{
while( condtion2 )
{
if (something_bad)
goto leave;
...
}
}
leave:
...

Indeed. The only caveat here is that labels like 'leave' must label
actual statements.
If there isn't one, e.g. if you're jumping into the end of a block, you
should create
an empty statement...

void foo(void)
{
while (cond1)
{
while (cond2)
{
while (cond3)
{
if (cond4) goto continue_while_1;
...
}
}
continue_while_1:
; /* ; (or equivalent) needed */
}
}
 
R

Richard Bos

Thad Smith said:
About 1968 or so Edsger Dijkstra wrote an article, published as a Letter
to the Editor in the ACM Communications magazine, titled Go To
Considered Harmful.

Actually, he didn't. When he wrote it, it was titled differently. Editor
of the ACM Considered Harmful.

Richard
 
M

Michael Wojcik

If anyone is studying C today, it is hard to imagine why they would use
goto in a substantial number of places, unless there is an especially
warped mind in place.

Is it really? A number of regulars here seem to have imagined it,
but perhaps we are particularly imaginative, or particularly warped.

I have a large code base where at least one goto appears in the
majority of the functions. They're all for handling errors with a
single point of return, but they definitely occupy "a substantial
number of places" in the code.

Now, that style may not be to your preference, but I submit that it's
hardly difficult to imagine why someone might employ it.
 
H

Herbert Rosenau

IME, there are cases where a goto is useful, but they are few and far
between.

Please name one! Only one. In 25 years of programming in C i have
never found a single one. I've written small, middle and really big
applications, I've written drivers, whole OSes for realtime
environments but have never found a reason to write goto.
I have no problems with gotos provided some basic rules are
followed:

1. Branch forward only.
2. Never branch into the middle of another control structure (if, for,
while, etc.)
3. Don't use goto if another control structure can do the same job.


ISTR some verbiage in H&S that the presence of a goto can hinder some
compiler optimizations.

In all of the 25 years I had to write code in highest security and
time critical environments whereasd any fault would have cost human
live.

There were only 3 rules to follow:
1. readability
not necessary for beginners in C but experts
2. security
avoid any obsurity
3. maintenacaiblity

All 3 rules contains avoiding goto by design, not code. Giving the
code a chance for goto is violating at least one of the 3 rules,
therefore a cause to redesign the sequence.


--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 
H

Herbert Rosenau

Counterexample:

preliminarysetup();
outer: while (condition1) {
dosumthing();
while (condition2) {
dosumthingelse();
if (condition3) goto outer;

Very bad design!
domore();
}
dostillmore();
}

The goto cannot be replaced by break or continue, and even if it
could the goto is clearer.

Redesign the whole block and you will avoid the label and the goto
statement implicite.
See the Knuth paper (reference from me upthread) for a
counterexample. s/Never/Almost never/


This one I can accept.

A good design will evacuate details of "how to do" into a deeper level
to hide it from "what is to do". That will increase the readability
and security by hiding details while increasing the maintenceability
and decreasing the time needed to debug the functionality.

isdoable = init();
while (isdoable && primary())
while(!!(isdoable = secondary())
if (!realwork()) {
if (errorhandling())
isdoable = FALSE;
break;
}
cleanup();
return;

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 
K

Keith Thompson

Herbert Rosenau said:
Please name one! Only one. In 25 years of programming in C i have
never found a single one. I've written small, middle and really big
applications, I've written drivers, whole OSes for realtime
environments but have never found a reason to write goto.

A goto statement is never necessary in a language that has a minimal
set of structured control constructs (there's a theorem to that
effect), but it *can* be useful at times.

For example:

void foo(void)
{
foo_type *foo = NULL;
bar_type *bar = NULL;
baz_type *baz = NULL;

foo = init_foo_type(); /* returns NULL on error */
if (foo == NULL) goto ERROR;

bar = init_bar_type();
if (bar == NULL) goto ERROR;

baz = init_baz_type();
if (baz == NULL) goto ERROR;

/*
* A bunch of code that uses foo, bar, and baz.
*/

ERROR:
if (baz != NULL) cleanup_baz_type(baz);
if (bar != NULL) cleanup_bar_type(bar);
if (foo != NULL) cleanup_foo_type(foo);
}

Of course it could be restructured. The most obvious way to do so is
to use nested if statements, with the nesting level becoming deeper as
the number of initializations increases. Another approach is to
replace each "goto ERROR;" with a return statement; the second and
third would have to be preceded by cleanup calls.
 
C

Chuck F.

.... snip objection to backward goto ...
Very bad design!


Redesign the whole block and you will avoid the label and the
goto statement implicite.

Oh - and how would you redesign it, bearing in mind that condition3
is in the nature of a local abort and that the emphasis is on doing
things? The idea is clarity, and I suspect whatever you are
thinking of will tend to obfuscate.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
 
R

Richard Tobin

Keith Thompson said:
void foo(void)
{
foo_type *foo = NULL;
bar_type *bar = NULL;
baz_type *baz = NULL;

foo = init_foo_type(); /* returns NULL on error */
if (foo == NULL) goto ERROR;

bar = init_bar_type();
if (bar == NULL) goto ERROR;

baz = init_baz_type();
if (baz == NULL) goto ERROR;

/*
* A bunch of code that uses foo, bar, and baz.
*/

ERROR:
if (baz != NULL) cleanup_baz_type(baz);
if (bar != NULL) cleanup_bar_type(bar);
if (foo != NULL) cleanup_foo_type(foo);
}
Of course it could be restructured. The most obvious way to do so is
to use nested if statements, with the nesting level becoming deeper as
the number of initializations increases.

Which (contrary to Herbert Rosenau's assertion) would make the code
less readable, at least to me.
Another approach is to
replace each "goto ERROR;" with a return statement; the second and
third would have to be preceded by cleanup calls.

Which would make maintenance harder and increase the risk of errors,
since the cleanup code would have to be maintained in both places.

A rigid rule against gotos is like the rule against split infinitives:
a fetish (cf Fowler's Modern English Usage). In both cases the
solution is not a rule but good taste and experience.

-- Richard
 
S

Skarmander

Herbert Rosenau wrote:
A good design will evacuate details of "how to do" into a deeper level
to hide it from "what is to do". That will increase the readability
and security by hiding details while increasing the maintenceability
and decreasing the time needed to debug the functionality.

isdoable = init();
while (isdoable && primary())
while(!!(isdoable = secondary())
if (!realwork()) {
if (errorhandling())
isdoable = FALSE;
break;
}
cleanup();
return;
Hurrah, we have avoided the evil goto. In the process, we demonstrate some
other readability-decreasing features: !! to "convert" to boolean, combining
assignment and test, omitting braces around multiply-nested statements.

This is a perfect example of when a labeled break would come in handy. C
does not have them; a goto would do.

if (!init()) goto do_cleanup;
while (primary()) {
while (secondary()) {
if (!realwork()) {
if (errorhandling()) goto do_cleanup;
break;
}
}
}
do_cleanup:
cleanup();
return;

Evacuating details notwithstanding, I can't come up with arguments as to why
a version that uses a control variable should be better design than one
without it.

Holy wars have been waged on this topic for a very long time now; although
one is free to prefer one style over another, it is wise to consider the
possibility that there are not altogether many rational arguments in favor
or against either side.

This example in particular boils down to whether you think goto is bad or
not. You can waffle about design all you want, but this is code at the most
basic level. Same nesting, same functions, almost the same line count --
what you prefer is really a matter of taste.

S.
 
R

Richard Heathfield

Chuck F. said:
Oh - and how would you redesign it, bearing in mind that condition3
is in the nature of a local abort and that the emphasis is on doing
things? The idea is clarity, and I suspect whatever you are
thinking of will tend to obfuscate.

I'd start off by choosing much better names.
 
H

Herbert Rosenau

A goto statement is never necessary in a language that has a minimal
set of structured control constructs (there's a theorem to that
effect), but it *can* be useful at times.

For example:

Crappy design:
void foo(void)
{
foo_type *foo = NULL;
bar_type *bar = NULL;
baz_type *baz = NULL;

foo = init_foo_type(); /* returns NULL on error */
if (foo == NULL) goto ERROR;

bar = init_bar_type();
if (bar == NULL) goto ERROR;

baz = init_baz_type();
if (baz == NULL) goto ERROR;

/*
* A bunch of code that uses foo, bar, and baz.
*/

ERROR:
if (baz != NULL) cleanup_baz_type(baz);
if (bar != NULL) cleanup_bar_type(bar);
if (foo != NULL) cleanup_foo_type(foo);
}

The same without goto:

if ((foo = init_foo_type())
&& (bar = init_bar_type())
&& (baz = init_baz_type())
) {
/* work */
}
cleanup_baz_type(baz);
cle......

or avoiding the big if and breaking the whole work down into different
levels, hiding too much details but giving better overview of the real
work:

static int init_types(int **foo, int **bar, int **baz) {
return (*foo = init_foo_type())
&& (*bar = init_bar_type())
&& (*baz = init_baz_type());
}

static void cleanup_types(.... like init_types but cleanup.......
:
:
:
void foo(void) {
foo_type *foo = NULL;
bar_type *bar = NULL;
baz_type *baz = NULL;

if (init_types(&foo, &bar, &baz)) {
do_work(....);
}
cleanup_types(&foo, &bar, &baz);
}

I would prefere the second choice when runtime is not really critical
because it makes anything more maintenanceable and more readable.
Of course it could be restructured. The most obvious way to do so is
to use nested if statements, with the nesting level becoming deeper as
the number of initializations increases. Another approach is to
replace each "goto ERROR;" with a return statement; the second and
third would have to be preceded by cleanup calls.
I would reduce the number of if statements and increasing the
readability, and the time testing is needed. When init is tested well
it can be ignored easy during forthgoing debug becaus it is known as
ready for GA. The same is for the real work......

I like to hide so much details as possible because it is after 3 years
untouched sources more easy to extend or even change the functionality
when details are hidden until there is a real need to know about them.
So looking at WHAT is going on and looking on HOW is it done only when
there is a need for makes anything much easier to understund.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 
H

Herbert Rosenau

... snip objection to backward goto ...

Oh - and how would you redesign it, bearing in mind that condition3
is in the nature of a local abort and that the emphasis is on doing
things? The idea is clarity, and I suspect whatever you are
thinking of will tend to obfuscate.

Break down the whole complicate thing in levels:
level description
1 show up WHAT is to do
when needed recurse level 1
2 how is it done
when needed recurse level 1 until
there is only one single step to do
or the number of steps is very short
and unconditional.

This can always be done by creating functions hiding details of what
is to do respective how is the detail done. This can (but must not)
reduce a number of if, for, while statements but will avoid any goto
by design.

Break down the whole program in a sequence of single steps. Code each
single step as own function whereas each function itself will
break down the whole function in a sequence wheras.....
...... until a single function fits on a screen size and has not more
direct nested control statemens as 3 or 4.

You would easy learn by that that C has some levels of visibility of
variables and functions and you'll learn to use that well.

After you has learned programming in C you would have to learn
programming using C. Then you would be ready to use C instead of
basic, FORTRAN or cobol or pure assembly and forget about goto and its
existence because you'll never see a need for.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 
H

Herbert Rosenau

Herbert Rosenau wrote:

Hurrah, we have avoided the evil goto. In the process, we demonstrate some
other readability-decreasing features: !! to "convert" to boolean, combining
assignment and test, omitting braces around multiply-nested statements.

This is a perfect example of when a labeled break would come in handy. C
does not have them; a goto would do.

if (!init()) goto do_cleanup;
while (primary()) {
while (secondary()) {
if (!realwork()) {
if (errorhandling()) goto do_cleanup;
break;
}
}
}
do_cleanup:
cleanup();
return;

Evacuating details notwithstanding, I can't come up with arguments as to why
a version that uses a control variable should be better design than one
without it.

Holy wars have been waged on this topic for a very long time now; although
one is free to prefer one style over another, it is wise to consider the
possibility that there are not altogether many rational arguments in favor
or against either side.

This example in particular boils down to whether you think goto is bad or
not. You can waffle about design all you want, but this is code at the most
basic level. Same nesting, same functions, almost the same line count --
what you prefer is really a matter of taste.

S.

You're right that my example is not the best. But in the short time I
found nothing that I were able to use to make from a nonsense a real
good design. For a blind hack of code that makes as such no sense it
was the best to show up other code without sense but avoiding goto.

Real design starts at much higher level and will avoid some constructs
before there is a chance to write a single statement.

!! is a possibility to save typing (and mistyping, e.g. like = instead
of ==). True, yes I do never take consideration of beginners in
learning C because one has to learn the language by using it.

Yes, I don't like empty lines whenever there is a chance to avoid one,
except an empty line can stand for self declaring documentation saying
there starts a new logical block of execution. Yes, I avoid {}
whenever possible because that gives up to 2 more lines on the same
screen page.
But for that my editor knows how to indent right and it will prove the
matching of braces every time I ask it for. True, this all is at least
a question of style but avoiding goto and longjump is never. You would
know that when you have written one singe project that has not only to
be failsave but has to save human live in any condition and not only
some money.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2 Deutsch ist da!
 

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,781
Messages
2,569,615
Members
45,295
Latest member
EmilG1510

Latest Threads

Top