Is goto Still Considered Harmful?

Discussion in 'C Programming' started by Lynn McGuire, Mar 12, 2014.

  1. Lynn McGuire

    Lynn McGuire Guest

    Lynn McGuire, Mar 12, 2014
    #1
    1. Advertisements

  2. Lynn McGuire

    James Kuyper Guest

    James Kuyper, Mar 12, 2014
    #2
    1. Advertisements

  3. I didn't see anything in that article that hasn't been said a zillion
    times before. I'll note that, although the code shown was C, the goto
    statements were used in very much the same way as most of the few gotos
    that I use in my Fortran code - for an error exit.
     
    Richard Maine, Mar 12, 2014
    #3
  4. Lynn McGuire

    Kaz Kylheku Guest

    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.
     
    Kaz Kylheku, Mar 12, 2014
    #4
  5. In your dreams, perhaps.

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

    ...
    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!
     
    Jos Bergervoet, Mar 12, 2014
    #5
  6. Lynn McGuire

    BartC Guest

    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.
     
    BartC, Mar 12, 2014
    #6
  7. Lynn McGuire

    Qolin Guest

    "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
     
    Qolin, Mar 12, 2014
    #7
  8. Lynn McGuire

    Lynn McGuire Guest

    Real programmers can write Fortran in any language.

    Lynn
     
    Lynn McGuire, Mar 12, 2014
    #8
  9. Lynn McGuire

    Stefan Ram Guest

    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.
     
    Stefan Ram, Mar 12, 2014
    #9
  10. 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.)
     
    Keith Thompson, Mar 12, 2014
    #10
  11. Lynn McGuire

    Stefan Ram Guest

    In C, a whole program of many MBytes source code can be written
    on a single line. The only exceptions are the preprocessor directives.
     
    Stefan Ram, Mar 12, 2014
    #11
  12. Lynn McGuire

    James Kuyper Guest


    "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".
     
    James Kuyper, Mar 12, 2014
    #12
  13. 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.
    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
     
    Andrew Cooper, Mar 12, 2014
    #13
  14. Amend that to:

    .... that can't *reasonably* be written on a single line.
     
    Keith Thompson, Mar 12, 2014
    #14
  15. 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.

    [...]
     
    Keith Thompson, Mar 12, 2014
    #15
  16. Lynn McGuire

    James Kuyper Guest

    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. :)
     
    James Kuyper, Mar 13, 2014
    #16
  17. Lynn McGuire

    Kaz Kylheku Guest

    Not always. There exist these things called unconditional branches,
    which are 100% taken. Wink!
     
    Kaz Kylheku, Mar 13, 2014
    #17
  18. Rick C. Hodgin, Mar 13, 2014
    #18
  19. Lynn McGuire

    Melzzzzz Guest

    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.
     
    Melzzzzz, Mar 13, 2014
    #19
  20. 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
     
    Jens Thoms Toerring, Mar 13, 2014
    #20
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.