When Dijkstra wrote his famous essay "Go To Statement Considered Harmful" (1968), it was a manifesto against unstructured programming i.e. the spaghetti code. However, the use of "goto" per se does not imply unstructured programming. Donald Knuth wrote a wonderful essay "Structured Programming with go to Statements" (1974) to make this point.
Availability of "goto" in C merely gives us more flexibility, but it does not mean that we should start writing unstructured code.
Few people have worked on real spaghetti code, thousands of lines with no functions, no modules, just spectacular leaps forward, backwards, leaping forward into the middle of huge loops, leaping backwards into the middle of loops, giant loops nested with and partially overlapping other loops.
I worked on such code, trying to decompose it in order to organize it into subroutines. It resisted my efforts almost completely. Fortran IV I think.
Users were agitating for `continue` to join `break` for control flow interruption. Lua instead chose to provide all the non-structured control flows you would like as a primitive; scoping it lexically keeps it from breaking composition.
I think the real reason why it hasn't been added to the language yet is that it has a weird interaction with the way Lua scopes its repeat-until loops. The justification about having a single control-flow structure is more of an excuse.
Since it's a keyword, you can't create your own ::break:: label, so that would be fine.
The closest I've come was my own code: minsweeper on my Ti-82 graphical calculator. I Separated the program in various sections, then used `goto` to jump where I needed. I was tempted at some point to use the "call program" facility instead, but that would have meant exposing those programs to the end users, so I just lumped everything in one file.
Reminds me why I love functions.
That makes no sense. goto is maligned because it's unstructured, the other constructs you list are structured and restricted. Much like loops, conditionals and function calls they're specific and "tamed".
Not only that, but the historical movement against goto happened in a context were goto was not just unstructured but unrestricted (to local functions).
Even K&R warns that goto is "infinitely abusable", and recommends against it when possible (aka outside of error handling & breaking from multiple loops as C does not have multi-level break).
With goto that's not guaranteed to be the case -- that was the spaghetti part.
vs.
if (False) throw ExceptionE;
if(False) longjump(error, 1);
vs
if (False) throw ExceptionE; switch (val)
while (cond)
{
case 42: /* wee: if val is 42, we go straight here */
break; /* this belongs to the while! */
}
I have written this kind of jig the past: if (...) {
} else switch (val) for(;;) {
}
:)(No, you can't use tail calls to represent state machines in C; that is not a feature of the C language but rather of a given implementation. And yes, you could model state machines with an enum variable and a big switch statement, but that's even harder to follow.)
What trips people up is when goto is used to jump across resource lifetime boundaries (which C++ addresses with RAII), when they maintain too much state in cross-state variables that they forget what their invariants are, and when they use a goto as a replacement for what should be a function call.
Using goto to implement e.g. a data processing loop, a non-recursive graph traversal algorithm, or a parsing state machine are all perfectly valid uses.
At a high level, this:
goto NewState;
is no less comprehensible or spaghetti-prone than this: state = NEW_STATE;
break;My uses of goto can be replaced with nested functions! The code is nicer, cleaner, and the equivalent code is generated (the nested functions get inlined).
Of course, nested functions aren't part of Standard C, but they are part of D-as-BetterC. (D has goto's too, but I don't need them anymore.)
https://dlang.org/spec/statement.html#continue-statement
also, what do you think of a partial compilation feature / tree shaking https://webpack.js.org/guides/tree-shaking/
YES it is dangerous due to stacks etc etc but hey, we're grown up adults, not script kiddies.
That is how CVEs are born.
C does not.
In practice, it doesn't. (The compiler inlines the code.) I know because I'm pretty picky about this sort of thing - it was why I was using goto in the first place.
> RAII
RAII can work, but it's a bit clunky compared to a nested function call. Additionally, if the code being factored is not necessarily the same on all paths, that doesn't fit well with RAII.
>RAII can work, but it's a bit clunky compared to a nested function call. Additionally, if the code being factored is not necessarily the same on all paths, that doesn't fit well with RAII.
I think I'd need to see an example of what you're talking about then because I don't quite understand how your method works exactly.
I guess you are not using goto as flow-control then.
That's a statement about what people say about the feature, not about the feature itself. The subtlety here is that malign as an adjective refers to something evil or ill-intentioned, while as a verb it means something closer to slander or defame. It's frequently (mostly?) used in a context of skepticism regarding the claims in question, especially in the construction much-maligned.
You just replace this:
void f(int n)
{
float *x = malloc(n * sizeof*x);
// ...
free(x);
}
with this: void f(int n)
{
float x[n];
// ...
}
edit: and you can exit your function anywhere without leaking.I just need to call your example function with the right n to corrupt the stack.
In the case of an operating system (the subject of TFA) pre-allocating everything is obviously completely impractical and alloca won't help since you can't return the memory outside of the stack frame. I'd wager that there are very few uses of goto in kernel code that could successfully be replaced by alloca (the fact that kernel stacks tend to be very shallow wouldn't help either).
With pre allocation you usually also end up creating your own resource management within the pre allocated pool and then you are back to the resource management problem...
As for continue, break, and return, they have an explicit workflow, existing or resuming the current scope.
The only language where unconditional jumps make sense is Assembly.
The last time I actually wrote a goto statement was probably in some BASIC dialect.
In Scheme, because a lambda expression is a closure and shall be optimized with tail call elimination, it is considered as the ultimate goto. It's goto but with procedural abstraction.
Even call/cc is more structured than plain gotos.
(define (goto proc)
(proc))On the other hand, using goto to jump back in the program flow, or multiple branching gotos are both asking for trouble.
I understand the how hamful goto statement is in general all of us know that. But there is very specific useful area in C. When Dijkstra wrote "GOTO Statement Considered Harmful", even before C was born, people tended to use goto statements everywhere because they were used to use assembly jump instructions. But we don't abuse goto statments anymore.
[1]: https://github.com/torvalds/linux/blob/master/net/socket.c#L...
goto is ok only for local error cleanup (or auto-generated code, e.g. for parsers etc).
EDIT: Or, for that matter, trusting the compiler to inline small-enough function calls?
A gcc extension that gets around macros is nested functions.
> trusting the compiler
99.99% of the time you should just trust the compiler to do the right thing. That 0.01% of the time use a well tested and maintained library.
Hating on goto in c is just cargo cult programming. goto in BASIC and FORTRAN are evil. Though back in the 1960's 1970's programmers often had no choice. Programmers used evil assembly goto hacks in order to get their programs to fit in memory/disk.
Problem with Dijkstra was he was a academic mathematician who despised practical programming where the program needed to run on the hardware available. Hint back in the 1970's professors like Dijkstra had unlimited accounts on the schools mainframe where everyone else had extremely limited accounts.
Perhaps goto is too valuable to let go; or letting it go results in code that is more complex than simply using it judiciously.
People who think "goto" is evil should also give up the other jump statements: continue, break, and return (and also switch, though its not listed as a jump instruction in the C standard, at least not in '89 or '99).
You can see some contradictions in the paper regarding goto. For example, they state that deep nesting should be avoided, but goto should be avoided as well, even though one benefit of using goto is to limit nesting depth. From the Linux Kernel coding style doc: - unconditional statements are easier to understand and follow - nesting is reduced - errors by not updating individual exit points when making modifications are prevented - saves the compiler work to optimize redundant code away ;)