18 ms·
Goto (2007)
- vadfa 5y agoLike any other tool in the arsenal of a programming language: when it best suits the task.
- gwbas1c 5y agoIn the past 20 years I've used a goto a few times, and then refactored it out once I was able to look at the problem with a clear head. I remember that, every time, I replaced the goto by breaking up a large method into smaller methods, and then replacing the goto with either return statements or logic that would essentially return. Yes, a goto is part of our programmers' toolbox. But, now even I consider a "good goto" a sign that a larger method should be broken into smaller ones.
- recursive 5y agoOften this is true, but those smaller methods might need to share so much local state that the solution is worse than the problem.
- Jtsummers 5y agoThis is where languages that permit nested functions come in handy. You can locally factor out new functions while preserving access to variables in the lexical scope without needing to pass them in as parameters or lift them into a higher level scope (object, class, global, file, etc.).
- recursive 5y agoThat's a good solution. The next problem is that many code patterns, such as state machines, if naively converted from `goto` to `call()` will consume a lot of unnecessary stack space. This might not be a problem with a language/compiler that supports tail call optimization.
- Jtsummers 5y agoRight. Absent TCE and inline functions (the latter is now pretty much bog standard in every language in popular use, at least every compiled language), goto for state machines and similar uses can be much more efficient and also handle the concern of blowing up your stack. If you have TCE and inline functions, then mutual recursion is a perfectly efficient way to handle that kind of situation that is often (but not always) clearer than goto. And if you pair that with nested functions so that you can close over some common lexical scope, you eliminate the need to use global variables or to thread data through each function call (keeping your parameters to a minimum).
- gwbas1c 5y agoPerhaps that's true when you're writing performance-critical code. For most code, readability trumps micro-optimizations.
- recursive 5y agoIn plenty of environments, there's a very limited number of calls you can make before the program fails entirely. For instance, in GW-BASIC, your namesake, the limit is less than 100 calls. There are plenty of real useful production langauges where your stack will run out before 1000 frames of depth. But on the other hand, it's quite reasonable to write a state machine which is expected to make [mb]illions of state transitions. Crash vs not-crash is not a micro-optimization.
- gwbas1c 5y agoOh you're bringing back memories of childhood. When I wrote in GW-Basic, I only used gotos. Most of the example code that I had access to used gotos. I was 11 years old at the time. > There are plenty of real useful production langauges where your stack will run out before 1000 frames of depth That wouldn't surprise me in an embedded environment. But if that's the case for a run of the mill general purpose programming language that's running a typical web application, I'd be shocked.
- 5y ago
- munificent 5y agoI tend to find that to be itself a code smell. But it happens. When it does, I like using local functions that just close over the same state.
- continuational 5y agoThat error handling is extremely error prone. It may be the only option you have in C, but that doesn't make it any better.
- einpoklum 5y agoCan't say that I'm enthusiastic about goto advice, but - for a second there, I thought the domain was bejeez.us :-P
- Grazester 5y agoWhen bit banging on a slower microcontroller I have found goto has it's uses.
- ainar-g 5y agoThe site seems to be hugged to death, at least to me. Archive link: https://web.archive.org/web/20211021120233/https://beej.us/guide/bgc/html/split/goto.html https://web.archive.org/web/20211021120233/https://beej.us/g....
- enriquto 5y agoComing from assembly language, I always found the anti-goto sentiment rather cute. A beautiful restriction, but ultimately arbitrary. Like writing poetry. Or those novels that do not ever use the letter "e". Why would an otherwise sane person write code with "rep" and without ever using "jmp"?
- mytailorisrich 5y agoIt's not arbitrary. The goal is to improve quality by making the code better structured and easier to follow. For instance, MISRA C rule 15.1 that states that goto should not be used explains: "Unconstrained use of goto can lead to programs that are unstructured and extremely difficult to understand". Indeed, in practice that's often the case.
- zabzonk 5y agoWell, in C you very rarely need it, but in assembler (particularly the 8-bit Z80 and 6502 that I've written raft-loads of code for) you definitely do. Of course, something like a CALL is better, if you can manage it.
- formerly_proven 5y agogoto is the idiomatic way to emulate RAII in C. It's everywhere.
- zabzonk 5y agoPlease explain a little more with an example. Certainly not in my C code, though I a huge fan of RAII in C++.
- panax 5y agoHere is an example with RAII and goto: https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consider+using+a+goto+chain+when+leaving+a+function+on+error+when+using+and+releasing+resources https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consi... Btw that is a Cert C recommendation to use goto for RAII! Take a look at the Linux kernel sometime, goto is everywhere there too https://www.kernel.org/doc/html/v4.10/process/coding-style.html#centralized-exiting-of-functions https://www.kernel.org/doc/html/v4.10/process/coding-style.h...
- me_me_me 5y agoThe only acceptable use-case for goto I came across was for error handling and cleanup. If there are multiple points of code failure that all require same cleanup procedure then one section at the bottom is acceptable and can actually keep the code cleaner. Anything else looks like bad design that ends up producing hard to follow code.
- mikevm 5y agoYes, this is pretty much how it is being used in C code. The other acceptable use-case is breaking out of nested loops. There's also `setjmp()`/`longjmp()` but that's even more rarely used.
- einpoklum 5y agolongjmp() is the C-language version of going-to labels in other functions. Its basic "use" is not having to unwind the stack; but moreover, it lets you avoid cleanup and resource de-allocation. Usually it doesn't make sense to do that (you get memory leaks), but if, say, your memory allocations happen in some kind of arena - you can just leave it in any junk state and then proceed to clear it altogether after your setjmp() instruction.
- SAI_Peregrinus 5y agoThe most common use of setjmp/longjmp I've seen (in embedded development) is to have coroutines in C. The original use AFAIK was usually to implement exceptions of some variety. Neither use is that common these days.
- zelos 5y agoI remember working with two C teams where one did: rc = do_something(); if (!rc) { rc = do_something_else(); } ... do_cleanup(); and the other did: rc = do_something(); if (rc) goto xout; rc = do_something_else(); ... xout: do_cleanup(); I think the goto was clearer and harder to get wrong?
- m4r35n357 5y agoYou can use static local variables with goto to implement yield semantics in c, look it up!
- 0xTJ 5y agoBlanket rules like "never use `goto`" are generally not great. A big part of the job of a software developer is knowing when to use what bits of a language. Sometimes code with `goto` is simply easier to understand. You have to be careful, but dismissing it completely is throwing out a tool.
- mikevm 5y agoPeople who give blanket rules such as never to use `goto` clearly do not understand where this idea comes from or why it should not be used. Many years ago in my undergrad I submitted a programming assignment in C which used `goto` for cleanup, as is customarily done in the Linux Kernel and other C software and I had points taken off for using `goto`. Heh.
- SAI_Peregrinus 5y agoShould've used setjmp/longjmp to do the same thing. Make it even harder to reason about.
- munificent 5y ago> People who give blanket rules such as never to use `goto` clearly do not understand where this idea comes from or why it should not be used. The idea comes from Dijkstra and he sure as hell did consider it a blanket rule: "I became convinced that the go to statement should be abolished from all 'higher level' programming languages (i.e. everything except, perhaps, machine code)."
- bgorman 5y agoC is portable machine code :)
- munificent 5y agoNot in Dijkstra's time it wasn't.
- krapp 5y ago
- Wildgoose 5y agoI remember getting "sniffy" comments for using a GOTO in my final year project, (which was written in Pascal). Basically, it was used to terminate the program without having to thread a return-value through very heavily nested code. I haven't needed to use a GOTO since. But almost 35 years later, I still maintain it was the correct decision at the time: a single GOTO statement was the clearest and most efficient choice.
- bigbillheck 5y agoStructured programming has won so thoroughly and become so pervasive that it's hard nowadays to understand the context in which Dijkstra was writing. 'Don't use goto' doesn't mean 'never ever use a goto under any circumstance', it means 'consider using this thing called a "while" loop instead'.
- munificent 5y ago> 'Don't use goto' doesn't mean 'never ever use a goto under any circumstance', This is simply not true. When Dijkstra said goto should be considered harmful, he literally meant that programming languages should not suppport it as a feature at all.
- hyperman1 5y agoThere are a few situations like this, where a bad practice is in a specific case good enough: Using goto to bail out multiple levels, using SHA-1 as a non-secure hash, ... The problem is these things waste social bandwith: Someone reading your code for maintenance or code review will first declare the code bad (GOTO is ugly! SHA-1 is insecure!), then you have to signal to them that it's actually OK in this case, then they have to convince themselves that actually it is OK. Hence I only do these things if they are overwhelmingly good. They have not only to be worth it to be written that way, but also be worth it to do the repeated discussion that they are worth it every few months. They deserve a huge comment, containing benchmarks or proof of security or whatever. If the construction is only slightly better, I go with the slightly worse construction.
- simion314 5y agoYou can link them to the manual, good languages documentation explain clear and when examples when is better to use GOTO. If there is an asshole in the team the official documentation would calm them down after they will be probably shocked that their cool language implemented this "evil" feature.
- leephillips 5y agoJulia’s approach is to not have GOTO as part of the language syntax, but to have @goto and @label macros. I feel that this strikes a good balance: it sort of discourages their routine use, but they’re there if you really need them.
- SAI_Peregrinus 5y ago> using SHA-1 as a non-secure hash, That I actually partly disagree with. There are much, MUCH better performing (faster, less memory use, etc) hashes than SHA1 if you don't need security. If you don't need security, and you don't need performance, and you don't have anything else available in libraries, then SHA1 is fine. But that's a pretty rare situation. SHA1 might not be a sign of insecurity, but it's often a sign of poorly thought out design.
- hyperman1 5y ago
- panax 5y agoWhen you are forced to follow stupid rules like having only one exit point in a function then goto can be used a lot in place of that, but what are we supposed to do if you are not allowed to both use goto or have multiple returns in a function (in C) ?
- zarzavat 5y ago> what are we supposed to do if you are not allowed to both use goto or have multiple returns in a function (in C) Move jobs. But seriously this is a good point. goto is practically necessary to manage memory in C. Having only one return point is justified in functions that manage heap memory. Standard C has no RAII, defer, finally, or any other alternative but goto.
- tyingq 5y agoPerl has goto, but it also supports labels you can apply to loops, and call for redo, next and last. Where "next" is like "continue" and "last" is like "break".
- billpg 5y agoMy favorite use of goto is with C#'s "yield return". The compiler converts a function with a yield command into a label. To resume a function from where it left off, the compiler adds code to test an added resumption-point variable and to goto the label for each resumption point.
- heavenlyblue 5y agoAll code that ends up being written as assembly will become a command (an opcode or a set of opcodes) and a label (the opcode's address).
- fjfaase 5y agoI have used the goto statement to implement enumerators classes in C++ using a state member and a switch statement with goto statements at the start of the method. Of course, also all local (loop) variables need to be replaced by (private) members. At each 'yield' location, you set the state variable, have a return statement, followed by a label. It is also possible to do this in C with functions and structs. See for example: https://www.iwriteiam.nl/Ha_cmt.html https://www.iwriteiam.nl/Ha_cmt.html
- woliveirajr 5y agoAt StackOverflow you're downvoted heavily when you just typ got (don't event have time for the last "o"). Then Apple comes with the glorious "goto fail" and shows that real companies use goto, and the problem wasn't "goto" itself.
- dleslie 5y agoIt seemed odd to me that their fail context didn't set 'err', but assumed it would be set by the origin of the jump. At least, I would have initialized 'err' to a failure value, so that any mistake that led it not to be set would result in a valid error state.
- bsenftner 5y agoMany C/C++ UI frameworks require allocating component parts, linking them together to create an operating widget, and then attaching callbacks and whatnot... The code creating a dialog/window/toolbar with multiple such widgets is a perfect case for using goto to implement a single code block of unwinding of the complex construction of that dialog/window/toolbar. Multiple times I've had long winded debates on why complex dynamic memory structure unrolling is safest as a single code block. The subtle win is the unexpected MASSIVE code shrinkage that occurs when a UI's codebase adopts goto for dynamic structure unrolling. I've seen applications that dragged become nimble if not perky after the code reduction from removing 2/3rds of the code that was nothing but repeats of partial structure unrolling due to an error at one spot, and a similar one a few lines down with a complete duplicate of the upper unrolling and a bit more added, and then another duplicate a new lines further, and again and again and again...
- ahartmetz 5y agoMaybe so in C, but in C++, stack-allocated container classes, smart pointers, scope guards(!), and (with Qt) the parent-child system can handle almost all cases. Binary size usually increases but lines of code are less or equal to a C-style solution. It's called RAII, but the main point is really that destructors are automatically called on scope exit.
- ladyattis 5y agoI think the issue is only relevant when considering the years when programming languages began to have more nuanced instructions. For Assembly, goto is an essential construct and you'll learn how to use it reliably as jump conditions. For C, it's a great way to get out of nested code that you couldn't avoid for the given problem. For more high level languages, continue and break are the 'goto' statements and they're used all over but they have built in restrictions which make them easy to use. Goto is just hard to master in use for some of us and it's okay to restrict ourselves to limited uses of it. I just don't know why it's become a mantra to say, "do not ever use goto or labels or jump conditions what so ever." It just seems odd to me.
- dec0dedab0de 5y agoFor more high level languages, continue and break are the 'goto' statements... I have never liked continue or break. It always felt like I messed something up if I found myself needing them.
- drodil 5y agoThe most amazing goto is: void main() { goto http; printf("Hello world!"); http://www.hello-world.com http://www.hello-world.com }
- sltkr 5y agoThis won't actually compile, even if you format it correctly, because a label must be followed by a statement.
- jll29 5y agoI like the presentation of the three nexted loops. Maybe "break;" should become a shortform for a generalized "break(1);" (= break one level) if we want to avoid using goto. Perhaps this can be implemented by combining a macro with setjmmp() and longjmp() from the standard C library, without requiring a compiler change.
- wruza 5y agoYep, some goto uses were ostracized without ever providing the alternative. In other languages breaking numerous loops is done by naming the one you want to control: outer: for () { for () { for () { break outer;
- jkcxn 5y agoIsn't it interesting that all of the examples in the original article can be replaced with a labeled break of arbitrary blocks? Even replacing the continue statement. Solving the problems of goto and keeping the behaviour. Not sure why more languages don't have this
- OskarS 5y agoThis is much more confusing to me than just using a goto. From your example, it's not immediately clear at all to me if you're breaking out of outer, or if you're breaking TO outer. You can figure it out with this simple example, but it's way more confusing than it needs to be. And importantly: way more confusing than just using goto. Given that break and continue are just gussied up gotos anyway, just put the label where you wanna go and goto it. I think it's one of the few perfectly valid uses goto.
- Jtsummers 5y agoThere is no confusion if you're already familiar with the anonymous break: // find in a loop for (a : array) { if (a == target) { print("Found it"); break; } } This breaks the loop. A named break is no different except that it names the loop that will be broken out of: find: for (a : array) { if (a == target) { print("Found it"); break find; } } Exactly the same as the previous, but we've named the loop for some reason. If you can understand this, then the case of named breaks (or named continues) with multiple nested loops are comprehensible with some effort spent writing or reading illustrative examples like the above. Why would it do anything else? A break is a break, it terminates the loop. To re-enter that loop would be a continue, not a break. > Given that break and continue are just gussied up gotos anyway, just put the label where you wanna go and goto it. I think it's one of the few perfectly valid uses goto. The reason not to do this is that goto's can (as illustrated in the submitted article) lead to some erroneous behavior that is harder or impossible to achieve with more structured equivalents (like named breaks and continues). Like, your goto can jump past variable initializations and get you undefined behavior. Using the structured equivalent of: for (a : array) { if (a == target) { print ("Found it"); goto found_it; } } found_it: ... Removes those potential errors from the system. That, of course, doesn't mean that goto should be forbidden entirely (plenty of examples in this discussion of where it's useful), or that something like the preceding wouldn't be useful in some circumstance. But if we follow the idea that you present ("break and continue are just gussied up gotos anyway") to its conclusion, we'd be back to the unstructured code that was Fortran and its contemporaries, because why should we have if, if-else, while, for, function calls, etc. when they're all just gussied up goto anyways?
- jugg1es 5y agoThe utility of goto is one of those things I've always understood but been too embarrassed to say out loud.
- GuB-42 5y agoI think we must distinguish between forward and backwards goto. And whether or not you are entering blocks. If you are not entering blocks, forward goto is usually fine, and for me, there is no good reason to think is is worse than break, continue and return. In fact, I would more readily ban mid-function return than this kind of goto. Backwards goto is when you get spaghetti code, there is some use for it, but it is very easy to make a mess. Entering blocks is particularly bad because it breaks both flow and initialization, I think if there is one kind of goto that should be considered harmful, that's the one, these can even break compilers. But as always, if there is a good reason to do it, why not, but you need a lot of convincing.
- garenp 5y agoThis is generally my sentiment as well. The reason to avoid using goto is if it makes following the flow of execution for us humans difficult, which typically happens when jumping backwards. Using goto judiciously to more easily escape a deeply nested block of code and/or jump to a cleanup section is not hard to follow.
- wruza 5y agoMost programmers and I believe even most teachers see technology not as an application, but as some sort of a blind religion. Shallow understanding only adds to this problem. I’ve seen enough of them saying “what, goto?” in disgust, but almost no one could explain why, apart from saying that it is a taboo and maybe remembering that it was in some book. They need it, and it’s really good that it was “goto”. If it wasn’t, they would jump on another, more useful bogeyman, like eval. And for those interested, the specifical use of goto that was considered harmful is its use in place of “for” and “[do-]while” flow control statements and explicit subroutines. Literally: loop: …; if (!cond) goto loop; a = 42; b = 13; goto sub; … sub: … It was used as such by inertia, because these were times before invention/adoption of the structured code. While you may not want to goto into inner scopes for obvious reasons, leaving two loops or goto-ing to a function cleanup section is perfectly okay and is much clearer than breaking the outer loop with a boolean or building ladders of ifs.
- userbinator 5y agoAnother good use of goto is to implement state machines --- "goto state_x" is clearer and simpler than the loop-switch that people often use. I can certainly say that over the years I've encountered code which was more difficult to understand and less efficient because either the author didn't want to use goto, or the language didn't have it. While it's true that you can always rewrite code to remove goto statements, it can definitely have a negative effect.
- beej71 5y agoOh, that's an interesting use I hadn't considered!
- JKCalhoun 5y ago> Multi-level Cleanup Used all the time a decade or so ago when writing code that made heavy use of Apple's CoreFoundation. CF could return nil (failure) for many operations, and memory management at that time was left up to the programmer. // Not real code, approximated from recollection bool createNestedCFThing () { CFDictionary *thing = nil; CFArray *arrayObj = nil; CFString *string1 = nil; CFString *string0 = nil; string0 = CFCreateString ("Hello"); if (string0 == nil) { goto bail; } string1 = CFCreateString ("World"); if (string1 == nil) { goto bail; } arrayObj = CFCreateArray (); if (arrayObj == nil) { goto bail; } if (CFArrayAddObject (arrayObj, string0) == false) { goto bail; } if (CFArrayAddObject (arrayObj, string1) == false) { goto bail; } thing = CFCreateDictionary (); if (thing == nil) { goto bail; } if (!CFDictionaryInsertObjectWithKey (thing, arrayObj, "somekey")) { goto bail; } bail: if (arrayObj != nil) { CFRelease (arrayObj); } if (string1 != nil) { CFRelease (string1); } if (string0 != nil) { CFRelease (string0); } return thing; }
- lmilcin 5y agoThis is my favorite use of goto. Not only it makes it more readable and easy to ensure the cleanup is only executed, it results in efficient binary representation which is important for some of the embedded stuff I am doing.
- mkipper 5y agoThis is the most common form of error handling used in the Linux kernel (at least the parts I look at). Although you'd normally have one error label for each allocation in reverse order, so you can jump to the cleanup step for the most recently allocated resource and pass through the other labels and clean them up as well. So in your example, it would be something like: bail_string1: CFRelease(string1); bail_string0: CFRelease(string0); bail_array: CFRelease(arrayObj); no_bail: return thing;
- vlovich123 5y agoManual memory management is bad but you can't escape it in C so `goto` is mildly useful there for that purpose. In all other languages you get better ergonomics. * In Rust you wouldn't need any of that boilerplate because lifetime is automatically managed (& Rust has Objc bindings). * If you turn on ARC & use the NS types you don't need this (they map to the same thing with no overhead for these primitives, so there's no good reason not to do this unless you are just being masochistic). * In ObjC++ you should still use ARC. But if you felt masochistic, you could do: auto string0 = unique_ptr<CFString, CFRelease>(CFCreateString("Hello")); auto string1 = unique_ptr<CFString, CFRelease>(CFCreateString("World")); auto arrayObj = unique_ptr<CFArray, CFRelease>(CFCreateMutableArray(kCFAllocatorDefault, 2, kCFTypeArrayCallBacks, kCFTypeDictionaryKeyCallBacks, )); auto thing = unique_ptr<CFDictionary, CFRelease>(CFCreateMutableDictionary(kCFAllocatorDefault, 1, kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks)); if (!arrayObj || !string0 || !string1 || !thing) { return nil; } if (!CFArrayAppendValue(arrayObj.get(), string0.get()) || !CFArrayAppendValue(arrayObj.get(), string1.get())) { return nil; } if (!CFDictionaryInsertObjectWithKey(thing.get(), arrayObj.get(), "somekey")) { return nil; } return thing.release() In C++ you could also use scope guards if you didn't like the `unique_ptr` stuff (not part of the stdlib yet, but it's not hard to roll your own). The point I'm trying to make is that there are much cleaner ways of obtaining that result in whatever language you choose. If you do that, maybe you too won't be responsible for a goto fail security flaw [1]. EDIT: BTW. This advise isn't out of the blue. This was 100% a strong culture even within Apple 5 years ago. If you're concerned about performance of ARC, consider that Apple dogfoods it internally for nearly every part of the OS. The only pieces it's not used are for large historical codebases where the migration cost is a factor. Performance of ARC vs manual just isn't something anyone looks at. For the historical codebases usually one would use the ObjC++ approach instead. [1] https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-goto-fail-apples-ssl-bug-explained-plus-an-unofficial-patch/ https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-got...
- t43562 5y agoI find that using goto for cleanups makes many functions much much clearer and I think it cuts bugs. Rather than writing the cleanup that is appropriate for each particular exit point you write one big cleanup at the end of the function that deals with everything. It adds some potentially unnecessary if statements (to see if some pointer was allocated or not before trying to free it or to see if some file handle was opened before closing it). To me it has always seemed well worth it - shortening the code and greatly simplifying the chains of error-handling if statements.
- bodyfour 5y agoProbably coming too late to the discussion, but commenting anyway... The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. Other than simple integer FOR loops, basically all control-flow in the FORTRAN of those days was accomplished with GOTO -- and to numbered lines, not labels. Things we take for granted in all languages today like { code blocks } didn't exist. Indeed the first thing you do when you try to understand code of that era is to print it out, get your markers out, and start drawing arrows all over the green-bar paper so you can start to make some sense of where control flow is going. In that world, yes, GOTO was a big problem and its use was rightly replaced with the structured control flow that we all use today. But that world has also been nearly gone for 30 years now. It's completely unrelated to using a C "goto" statement with a well-chosen label name in order to accomplish some otherwise-awkward control flow. On projects I've worked on I tend to develop a reputation for heavy goto use and I do so unapologetically. Sometimes it honestly is the cleanest way to implement something and I don't think it should be feared one bit. Just because it was a bad idea to do everything with a GOTO 60 years ago shouldn't mean don't do some things with goto today.
- rstuart4133 5y ago> Indeed the first thing you do when you try to understand code of that era is to print it out, get your markers out, and start drawing arrows all over the green-bar paper so you can start to make some sense of where control flow is going. The first thing I do is change the indentation to reflect the structure. The basic blocks then become obvious. If you don't do that (and most people in the era didn't), it's about as hard to read as left aligned C code. With it, it's not that much harder to read than indented C code. Unfortunately not indenting the code just reflected a bigger problem: programmers back then were totally oblivious to the structure indenting revealed or how they could be used to reduce the difficulty in reading the code: basic blocks, limited scopes and the like. It's not that they were lazy, it's that they didn't understand how code is built. Dijkstra taught computer science. That is what he was up against. At the time, all programming languages used goto almost exclusively for flow control. In some ways, it is the simplest way to do it. One construct did the job of the multitudes we use today: for, while, until and even recursion. However if you didn't follow some simple rules the goto's invariably ended up as spaghetti and the end result ended up being there was far more spaghetti in production than structured code. The two simple rules are: 1. backward goto's were only for loops, and 2. you must not goto inside an inner basic block. These are exact same rules for and while loops with {} blocks enforce. But it takes some self discipline to apply those rules, and no one is going to do it if they don't understand why they are important. Such understanding requires good high level intuition about how code is structured. It took a long while to teach such intuition. If you got rid of the goto's by replacing them with higher level constructs like for and while, you not only got rid of the problem - you also forced young students to think in terms of code flow. That is what Dijkstra was arguing for in "Goto's considered harmful". Today, times have changed. You very rarely see a badly structured goto in the Linux kernel code, even though C allows it. I'm not sure why that is - because I'm pretty sure if Dijkstra hadn't won out and Javascript and it's ilk still had goto's then shudder. The coding standards in web programming is a cluster fuck as it is: I swear there isn't a week go by when I don't come up against a broken web page. Just this week I was forced to fire up the browsers js debugger to change a field on a linkedin.com form, and manually do "$0.value = 'yes'" to move on. ffs. But yes, in projects with coding standards as high as the Linux kernels, everybody that (is allowed to?) contribute seems to have a very good understanding of how to make code that others find easy to read and understand, a ban on gotos would be counterproductive. Despite the discipline they require, gotos can be used to make the code cleaner and shorter. You see numerous examples of that in the kernel.
- beej71 5y agoOh good, I thought! Maybe there are some ideas here I could add to the book! :)
- buescher 5y agoFor those that haven't seen Knuth's "Structured Programming with go to Statements" before: https://pic.plover.com/knuth-GOTO.pdf https://pic.plover.com/knuth-GOTO.pdf Enjoy!
- ravenstine 5y agoGoto probably doesn't have much of a place in structured programming, but I think there are potential applications for it in more finite domains. Though I never finished it, years ago I was working on a language specifically designed for voice activated devices, interactive fiction games, and so on. I wasn't happy writing Alexa skills using languages like JavaScript because such languages have more features and complexity than are ever used for a voice interface. So I worked on a lexer-parser for a language I invented that was very minimal. These were the features of the language: - No syntactical scoping. You have to be explicit about the "data set" you are using. - Close to being more like an "assembly" language, every line being a command with parameters, but is human readable and declarative, and supports expressions. - Control flow is done not only through the equivalent of a "goto", but something like "goto chapter" and "goto section". - There is no syntactical concept of loops. Loops are effectively performed using gotos and conditions. - Lines beginning with whitespace are interpreted as text or speech to be rendered. For the purpose of what I wanted to do, this allowed me to focus more on the user experience and less on fitting my ideas into languages designed to support algorithms, OOP, and so on. The simplicity of using gotos meant less conceptual overhead; everything could be done with just if-else, goto, math operators, and negation. I imagine goto may also be a good way to introduce young kids to the concept of programming, though I suppose that could backfire if they get too used to writing unstructured programs.
- marcodiego 5y agoActually "GOTO Considered Harmful" should be considered harmful. But goto should but not used by beginners, or else they get bad habits. Also, people should consider that there is a proposal for "break break" in C2X: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2859.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2859.pdf
- DeathArrow 5y agoI learned Basic in the 90s and there was no for, no while, no functions or procedures. Just ifs and goto. Its hard to structure a program just with goto and it's even harder to read it and understand the flow.
- deleted 5y ago[deleted]
- kazinator 5y agoGoto is a useful precursor for writing a tail recursive solution. For instance, say we have a state machine for recognizing that the last four button presses were 1234. int keypad() // returns 1 if correct key is entered, otherwise loops forever { keypad: switch (getkey()) { case 1: goto got_1; default: goto keypad; } got_1: switch (getkey()) { case 2: goto got_2; case 1: goto got_1; default: goto keypad; } got_2: switch (getkey()) { case 3: goto got_3; case 1: goto got_1; default: goto keypad; } got_3: switch (getkey()) { case 4: return 1; case 1: goto got_1; default: goto keypad; } } Now, refactor the same structure into tail calls. Now it's suddenly beyond reproach. int got_1(); int got_2(); int got_3(); int keypad() { switch (getkey()) { case 1: return got_1(); default: return keypad(); } } int got_1() { switch (getkey()) { case 1: return got_1(); case 2: return got_2(); default: return keypad(); } } int got_2() { switch (getkey()) { case 3: return got_3(); case 1: return got_1(); default: return keypad(); } } int got_3() { switch (getkey()) { case 4: return 1; case 1: return got_1(); default: return keypad(); } } You can refactor any flow control graph based on goto, with local variables, into a network of tail calling functions. The tail call graph is isomorphic to the goto: it's just as complex and hard to understand. Yet it has the virtue of being beyond the reproach of computer science academia.
- ladberg 5y agoI think the better way to create a state machine would use another switch instead of the top-level goto like: int keypad() { int state = 0; // 0 = start, 1 = seen '1', 2 = seen '12', 3 = seen '123' while (true) { int key = getkey(); switch (state) { case 0: switch (key) { case 1: state = 1; default: state = 0; } break; } case 1: switch (key) { case 1: state = 1; case 2: state = 2; default: state = 0; } break; } case 2: switch (key) { case 3: state = 3; case 1: state = 1; default: state = 0; } break; } case 3: switch (key) { case 4: return 1; case 1: state = 1; default: state = 0; } break; } } } In my mind that reads more obviously as a state machine than a bunch of gotos and labels (and should function the same assuming I typed it correctly, I haven't run it anywhere). It's easy to see each state and how it flows into other states.
- deleted 5y ago[deleted]
- N0tAThr0wAway 5y agoGoto is like a tool in your tool shed that may only be used once a year or less. It has an extremely narrow use-case, can be dangerous is used improperly, and may require more experience and training versus using your run of the mill pipe wrench. I always hated the platitudes that people use when talking about gotos. Yes it definitely can be dangerous, yes it's probably best to refactor a specific area of your codebase to not use them, but every once in a while, it's the right tool for the right job.
- dang 5y agoI put 2007 above because the book it's from seems to be from then. Beej’s Guide to C Programming [pdf] - https://news.ycombinator.com/item?id=26911399 https://news.ycombinator.com/item?id=26911399 - April 2021 (171 comments) Beej's Guide to C Programming - https://news.ycombinator.com/item?id=26100391 https://news.ycombinator.com/item?id=26100391 - Feb 2021 (1 comment) Beej's Guide to C Programming (2007) - https://news.ycombinator.com/item?id=15198093 https://news.ycombinator.com/item?id=15198093 - Sept 2017 (79 comments) Beej's Guide to C Programming - https://news.ycombinator.com/item?id=8295024 https://news.ycombinator.com/item?id=8295024 - Sept 2014 (1 comment)