What standard of C do you use in this course? I hope it's ANSI c89. I really enjoy your videos, you are one of the persons that inspired me to learn C. And the project you've picked is just brilliant. I just wish to finish Herbert Schildt C++ Reference (only C part) and Dennis Ritchie C programming language books before starting your course, I hope it makes sense
There is an other way where using goto is more readable. Make break with multiple loop. Without goto, we have to define an exit variable, and the test statement of the loop looks unreadable.
I've been programming for over 50 years and goto has a definite place in the toolbox. Now for highly structured languages such as C/C++, Java, etc. goto can be somewhat confusing. But for procedural languages such as cobol (yes, cobol is still used today), Fortran, etc. goto can actually make the code more readable and, moreover, more efficient. For assembly language, goto is a necessity.
It annoys me when people group C and C++ together as though they're the same language. It was already easy to make a program that was valid in one but invalid in the other by C99, but now it's nearly impossible to not hit that wall. Java kind of pisses me off due to how little foresight they had in designing it. Not merely removing goto but making it a reserved keyword that generates an error at compile time, and then they had to add functionality to break and continue just to make up for the deficiency which caused a lot of problems that C++ already solved but in a more elegant way while still allowing you to use goto for those problems where it was still somewhat necessary. I guess what I'm driving at is that these ideological stances that people take based on inexperienced programmers misusing tools leads to garbage language design as well as garbage program design and I wish the lies that perpetuate them would stop cropping up. The old Knuth quote about premature optimization really irks me too.
@@bb-sky But it *is* telling it to continue, from the top of the loop. Though, I wouldn't be opposed to using next as the keyword to start the next iteration of a for loop, it wouldn't make sense in the context of a while or do/while loop, and in the interest of lowering the keyword count I'd delete the one that's the odd man out.
@@bb-sky Interesting perspective. But you'll have to come up with a better word than next or skip to convince me. I can't think of a word that's more apt than continue, only phrases.
You can also write a function that does this for you and call that function with different parameters if you are scared of the unstoppable errors that might come with this approach.
Splitting the logic of the function with it's error path in another function tends to make things much messier and make easier bug as you need to sync the error with your function in any case. You also might pass a crazy amount of params of your function to your error function !
Yeah agree. The error handling case mentioned here and sometimes breaking multi-layers of for loops is where goto make more sense than normal control flow. They are literally everywhere even in source code of linux kernel.
@@monad_tcp try/finally is not normal control flow but exception handling, which requires quite a lot of runtime support. C is just too low level for that. That's not to say you can't implement it in C, but it's not part of the standard language because that is designed to be portable across a huge amount of different systems. Try/finally requires stack unwinding, which is very system specific. With gcc, exception handling for C++ is actually implemented in C. Under the hood of course, all control flow eventually boils down to a form of 'goto'.
@@jbird4478 try/catch/finally is control flow. It changes the point where the execution of code is going, therefore it is a control flow statement, compared with all the other nodes in the AST that are considered expressions, not statements. I'm using a formal definition here. (not the C programming language specification, but more general) You might correctly argue that C is low level for that sort of control flow, you might be right, C is a "mere" macro-assembler. On the other hand, even function calls might be considered special "control flow" as the C programming language does run in hardware without a stack. All of that is merely an implementation detail. But you do have a point.
I've only had to use a goto once in ten years, to escape a highly-nested for loop. I could have "unrolled" the for loop and turned it into 5 functions, but I tried that, and it resulted in a code that was less readable than with a simple goto. My boss fought me for it, but in the end, dogmas and tradition matter less than producing code our teams can actually use and maintain easily.
@@dracheflydepends on the language, some languages support specifying now many loops to break, which is error prone, and/or some have labeled breaks, while others don't. I would say goto is essentially a labeled break, since this is the only real use I see in it in 99.9% of cases. It is still clean because the jump is directly after the loop to be broken, so you don't make spaghetti like this video made.
Imo discussing why GOTO is shunned without talking about what it actually was when "GOTO considered harmful" was written does the discussion a misservice. So, GOTO at the time a lot more powerful than what C is able to do. It was not only in most languages the primary way of handling control flow, it was also able to jump across function boundaries. Here an example (in C syntax because reasons): void f(int i) { label: print(i); } void g() { goto label; } This lead to the problem that you couldn't think of function like a black box. You needed to know what it does if you want to reason about your code. And because EVERY function was able to do that, you needed to practically know the contents of EVERY function. And guess what, stuff like this was so widely done, that at the time "GOTO considered harmful" was published it was highly controversial ("You want to say I am not capable of doing this safely?").
That's good to know, I didn't consider it used to work like this. I was always under the impression that the label has to be in scope, so it's not easy to abuse it. Maybe in older versions of C and compilers without following the standards. And the article would make sense then if people did this regularly. I don't even know how that would work, because if in your example g() jumped to label in f(int i), then what the hell is the value of i at that point? Why would anyone do that even if allowed? But thinking about, we have much worse things nowadays that will actually break the flow, something inside scope affecting things outside scope even without you realizing it.
@@xwtek3505 as I said, in C it would not even compile in the languages back then? well, UB wasn't really termed well yet and everything not otherwise defined was "implementation defined behaviour"; so, yeah, it worked because programmers looked at what the implementation actually did, and then used that to their advantage not with undefined behaviour, that's not really possible anymore since what is being done under the hood is more often than not unpredictable I would argue that we are worse off know in that department nonetheless, goto thankfully can't jump outside of functions anymore
I think scope restrictions are a major part of why GOTO got deprecated to begin with. Even at the lowest level, if you GOTO a code point that's inside a different function scope, then it encounters a RETURN (without having first called said function in the usual manner) the return address it pops from the stack may be anything from unpredictable to actual garbage -- likewise, references to local variables pushed onto the stack may return unpredictable/garbage values.
In C, handling errors in resource acquire/release is already error prone, and goto specifically for this case and only for forward jumps reduces human error. This is a common pattern in kernel code itself. If you're using C++, you'd be doing it wrong by using goto since we've got Resource Acquisition Is Initialization there.
@@gustavoholo1007 my day job for the past decade has been as a professional software engineer. Studied computer engineering in college and grad school with a focus on computer architecture, but didn't get along well with hardware design, so here I am as a programmer instead
@@kevinyonan9666You only have one way to check for error in rust, instead of checking for `NULL`, `-1`, `VALUE_ERR`, or whatnot, and then reading the `errno` or whatever mechanism is used to know what the error was. In rust you just add a `?` or you use one of the error handling methods provided by Option and Result - with most of them sharing the same name across the two types.
Finally someone (else) that shows evidence where goto's are useful. Folks, goto's aren't evil; they're just another tool in the toolbox to use when it makes sense given all of the factors. (Singletons in other languages are similar in this way.) Having said this, part of determining "when it makes sense" is to not take it too far and abuse it.
People want shorthands that let them avoid thinking. Every tool is an asset, and you should have better reasons to use them or not than "someone told me so".
*"not to take it too far and abuse it"* I've been having some discussions with some Python programmers and they've been suggesting doing things like using the exception system for things other than raising and handling exceptions. It was a good reminder as to why things like GOTO are considered bad practice - if you metaphorically give some programmers a gun they will eventually shoot themselves. That said, GOTO in the event of an unhandleable exception in C is probably itself an acceptable 'exception' given the lack of a formal mechanism.
@@loc4725 Using exceptions for other things is very common in Python. You'll often see usage of 'KeyError' for example when something not being in a dictionary is just a normal expected state. I don't like it (but I don't like most of Python), but it's not a reason to ban exceptions from Python. Likewise, misuse of the goto statement is not a reason to ban it. You should just accept that some programmers will shoot themselves in the foot. There's no such thing as a good and also idiot-proof programming language.
@@jbird4478 There do appear to be quite a few, I guess you could call them 'mistakes' in Python. Looking at it's development history is like watching people learn what makes a language good / bad but with little knowledge of what came before and why certain features were dropped, added or made that way. Also I wouldn't "ban" exceptions and neither did I say that was a good idea. My problem stems from experience of how certain language features or paradigms end up being a problem because you cannot trust many programmers not to abuse them. And yes I agree, all you can do is limit the damage but then that is what a good high-level programing language should aspire to do. It shouldn't needlessly place scalpels on the dinner table or worse, force people to use them because there's no other choice.
You're 100% right .. Read any major projects that power major infrastructure (all C... Hmm) ... You'll see lots of goto, always down to an error condition. It's a pretty common trope in C. Nothing wrong with it.
@@MrAB-fo7zk Not *always* down to an error condition, sometimes you just want to skip over a block of code because a condition is met and it's cleaner than nesting that block within a large if clause. Bottom line though is that as long as you are using it to move down from where you are at the same level of brackets, you are absolutely fine. It only gets sketchy when you start using it to go up (IE creating a loop) or to the right (IE you are jumping into a condition or a loop). That's where people start getting themselves into trouble.
@@MrAB-fo7zk That is because a GOTO is a excellent way to abruptly escape out of a function or even the whole program when a serious error occurs. Java does something similar with Unchecked Exceptions (aka a runtime exception), which the programmer is not required to handle. A very common example to come across is the NullPointerException, which usually means that a reference which was expected to point to a valid object is /null/. E.g. It's like a restaurant receiving a takeout order without any customer information or which either contains items that aren't on the menu or none at all. --- I.e. there's little nothing you can do with it besides discard it
My favorite use for goto in *C++* is to mimick the for-else pattern from Python. For the unaware, else statements on for loops will only execute if the loop concluded by its condition, rather than a break. With iterator-based for loops, it's as simple as moving the condition inside the loop body and turning it into a break (that way, you can add additional code to that specific exit path). For range-based for loops, that is not an option. Instead, the solution is to replace all breaks with gotos which skip past a block of code immediately following the for loop's scope. for (const auto& v : container) { if (some condition) goto LOOP_EXIT; SomeFunction(v); } std::println("All good!"); LOOP_EXIT: ...
you could also do the following and skip using goto, although goto is cool. uint16_t i = 0; while (i < loop_range && !(some condition)) { SomeFunction(i); } //destroy i
@@PalladinPokerI don't think that code does anything like what I wrote, but I think you were trying to suggest using the loop counter from outside the while loop's scope to detect if it terminated before reaching the loop_range? GOTO doesn't require an if statement to check the loop counter after the loop concludes, so GOTO is still better if that's what you're trying to suggest.
Don't be afraid of gotos. At the core, the goto statement represents one of the most fundamental building blocks of computing; the jump. Without jumps there would not be touring completeness. I love the fact that C allows you to directly use these very low level bricks, as you can implement control flows that are impossible with ifs, switches, whiles and for loops. Once again, C gets you closer to your hardware and that's a feature, not a bug.
All you need for Turing completeness is if-statement and while loop (both if these use goto under the hood). In a small number of scenarios, goto might make your control flow simpler, but you can always achieve the same thing without it.
@@lukekurlandski7653 Yes! That's almost right! You don't actually need while loops, just if-statement and goto-statement actually. In languages like BASIC you don't get a built in loop statement, you just use ifs and gotos to go back en repeat the loop until a variable reaches a certain value. I kind of like that because it reflects how your computer actually execute your loops under the hood.
@@georgehelyar Yes, but there's also no reason to pollute your interface with more functions if there is only one user of said function. One of the reasons why goto has become this hated thing is because goto was the cool thing to do at one point, which just sucked ass when done on a larger scale and very long jumps. Doing it to break out of nested loops or similar is totally fine.
@@js46644 How would it not be? If you suggest making the visibility private, reconsider that thought and think about what an interface is. I do not mean the keyword interface you're probably thinking of.
@@js46644 I agree with you, there is no reason to make the function part of the interface. It should just be a static free function, a private static member function, or not exported from a module.
In general as a rule of thumb, I try to keep GOTOs in the same function scope, so just for loops and the like, it helps keep GOTO reasonably easy to read (and far safer!) but also still gives it a lot of flexibility. Also if I'm writing optimized code, I try to keep branches in general (not specifically GOTOs, they just tend to get messier), to be short and minimal, only things like small if statements, just to avoid the LOADS of cache misses you can cause by putting everything in their own function.
Note that in C, goto cannot be used to jump into a different function (mostly because it would be unclear what should happen when that function tries to return). You can use longjmp to do that, but only if the function is already on the call stack (i.e. it behaves a little bit like throwing an exception, but without proper language support). My advice is to avoid using backward gotos (prefer writing a loop), and to only use forward gotos to eliminate excessive nesting of if statements or duplicate code. Hand-constructed goto loops can be very difficult to understand, and simple if statements are usually more readable than if-goto compounds.
goto has one really important use for me that aligns with this- it's one of the only ways to short circuit out of a large nested loop algorithm without having to explicitly end each loop.
@@xwtek3505 c++ (at least the mingw compiler from experience, but I think any flavor) can do goto from any point to any other point and can cross scope. it is almost always undefined behavior to goto from one scope to another. notable examples of where it can be used is as a return from a void function to the main scope, calling into a function that returns void and uses no arguments, but you have to goto back out, and, it can be used to navigate self altering code if you have the balls to write it. I will use goto to leave nested loops if there's no good way and I'm relying on local variables heavily in the destination rather than refactoring it into a referencing function. c++, and c, will let you do all kinds of crazy bs. almost every combination of characters that compiles will result in a crashing program. it takes very specific sets of characters to result in a stable execution. there are many things you can code in c that will result in an undefined behavior, but a repeatabe result, and that gets used for what it actually does, whatever that is. if you want a Lang that doesn't spit out an executable if it has undefined behavior- go use rust and run into compiler walls
@@xwtek3505 yes, c, c++ both allow you to goto between scopes. it's practically always undefined behavior. notable exceptions are as quasi function calls but you need to save the origin, and self altering code, if you have the coconuts --at least the mingw compiler will allow you to compile with these using certain flags.
I always love this argument. Writing high level code without GOTOs creates more readable code. This is mostly true. The funny part is, the compiler generates tons of jumps (gotos) in the final binary, the actual assembly language. Just disassemble a switch statement, or if-then-else. JMP (goto) is there.
just because the compiler does it for you doesn't make it a good practice. Source code is meant to be readable, so make sure it is. If it wasn't we would all be programming in binary. Because that's what the compiler ultimately makes of it.
@@iWhacko @JodyBruchon I think you replier's are missing my point. I've written code with high-level, mid-level, and low level languages for 35 years on a variety of embedded platforms and various computer systems and have always followed the standards of well written, highly commented code as every programmer should, so I will repeat this one little part again. I still find it funny that so much emphasis is put on NOT using gotos in code, even though the compiler has NO choice but to put jumps/gotos into the code at the BINARY level to make things actually work. So, at the lowest level, this no goto philosophy is bunk. Really good compilers do take into account the number of times an instruction cache would be cleared from branches, jumps, calls, and the level of optimizations selected during compile time can mitigate this problem. Some of the compliers are really really good at this, by actually counting instruction cycles and choosing the shortest paths. The bottom line is this. Assembly language can not exist without a jump instruction, but high-level/mid-level code can exist very happily without any goto statements.
@@iWhacko " Source code is meant to be readable". I have always stand on position that the source code is meant to create efficient and error-free binary. Readability is secondary. I have experience in nightmare projects that were killed by "good practices" and "code standards" and "clean code". Classic "measure becomes a goal" cases. Deadlines? Don't care, havta extract 5 more interfaces that will only ever have 1 implementation. Performance? Sorry, too busy renaming half of the functions in the project by adding "get" in front of the name. Bugs? Can't help, i am injecting 78th undebuggable maven dependency that obscures the logic behind reflection. Meanwhile i was able to recreate 80% of the project from scratch on my own spare time, using 50 classes that compiled in 10s. Not 2000 AbstractIntgerStringPrinterFactory classes that compiled for 15 minutes because of 2 GB of maven dependencies. But it was all "good practicies" while my code was too "tightly coupled" so they went with continuing the garbage. Project went under because of poor performance, bugs and crossed deadlines. Imagine my shock.
In pure C stuff like this is an at least decent to good way of handling things. In most other languages you should rather use some sort of scope guards though (something RAII-like like objects in C++ and rust, or "using" resource statements in C# etc.)
And even if more modern low-level-like-c language like Zig where you don't have RAII (because no dtor) they added the statement defer and errdefer exactly to avoid this kind of code.
I think in C++ goto statements are also more complicated due to it being difficult to reason about RAII and object lifetimes in goto. At least I don't remember off the top of my head what the behavior is and I think a lot of programmers don't.
Thank you for making this video. Many people always scream at the sight of a goto without considering how it can be beneficial to the circumstance. I personally write C# more than anything else so I benefit from 'using' statements, however I still find gotos can be very useful when writing state machines or non-LINQ filtering logic.
In everyday's code I barely use goto, but when doing code challenges like LeetCode or CodeWars using C# where I tend to stay away from Linq (it's slow + it trivializes some challenges), that's when "goto" statement feels the most useful. It's more efficient both space-wise and time-wise.
This has little to do with 'using' - in C goto may be helpful because it lacks control structures like try-finally and its APIs rely so heavily on return codes. Which is a nice way of saying that C lacks proper tools for certain things, and goto is just the lesser of a bunch of evils. But that does not make it good or safe. When you have a language that gives you better options, use the better options. Goto breaks structured programming, and static analysis tools can't do their job properly without being able to make strong assumptions about the program flow. In C that may not be a big deal because you cannot make these kind of assumptions to begin with. But in modern C# static analysis is a big advantage. Tools rely heavily on knowing when something has been initialized and not, for example. In my experience many C folks cannot even appreciate that to its full extent because they have no experience with it. It would be foolish to compromise that just for something that might optimize the odd case by a few lines - and this is the reason why goto gets banned in so many projects. Please note, it's not at all about whether goto is sometimes useful. It's about principles: If a feature is a bit useful, but sometimes dangerous, and there is another option - always use the other option. Don't argue with me, Douglas Crockford says that a lot, and he's probably smarter than most people here. If you ever find yourself writing programs that look like that in Java or C#, step away from the computer. I don't argue about C, but as a senior lead developer and architect with 25+ years of experience, I say this with confidence: I have not seen one justifyable reason to use goto in C# application programming. Not even in 3D visualization or device driver code for industrial hardware.
goto's are very handy in the case of gamedev - specifically when wanting to jump around within nested loops. Makes things 10x faster and organized if you do it right
If you must use goto (usually to do something the language doesn't support, like breaking out of nested loops), use it sparingly and make it as clear as possible what it is doing, by using descriptive label names.
2:33: No, you print first so you are sure errno is not modified. Now, in this case there is no reason that errno would be modified, but that's not always the case.
I don't mind goto at all, but I think it should only be used in a very small scope. I think you can usually get a similar result through creating functions, so there is little need to actually have goto, but imagine you're searching for a specific value in a larger function and you have multiple nested loops for whatever reason. At that point you might actually want to have a goto in order to quickly break out of all loops. That is a perfectly valid use case in my eyes. The label is close enough to the goto for things to be understandable enough and you avoid having loads of breaks and if-statements. Could you just have made a function and returned the value when finding it? Sure. But sometimes you don't want to create a new function as it is not really a necessity in your use case. The goto will also be faster than doing if-statements every loop that break if true, which can matter a lot for larger data sets. So as long as you keep things simple enough, I think using goto isn't bad. Just don't do a bunch of branching logic with them. Only use them as fast exits out of nested loops and such and you're all good, avoid them if necessary though.
Breaking nested loops is the pretty much the only reason I see to keep goto around. There are some other possible very niche uses, but generally this can be improved by refactoring code to be cleaner. In this video, he turned 13 lines of code into 17, and made a mess out of it.
The proper answer to this code chaos is (hold on tight): nest your code. Yes, you read that correctly: nest your code! You started of quite nicely by introducing a return variable. This should ALWAYS be the very first declared variable in your code and set to THE error code, here -1. On success, the inner level of nesting, set it to 0. This saves a whole lot of lines assigning -1 to the error code variable. Furthermore nesting provides a perfect way to achieve congruency between scope and lifetime of a variable. Actually it's the only way.
Once upon a time I worked like that. We called 'epilogue' to labels at the bottom of each function to handle cleanup and having a single point of return. When goto was considered a 'bad word', exceptions were invented. Great video to remind that goto is not so bad, thank you
bool do_something_unguard(int *f) { } bool do_something() { bool b; int f = open(); if (f) b = do_something_unguard(f); close(f); return b; } // this gets boring fast, so does "goto exit:;" // its almost like there's a reason for try/finally to exist...
I always found it an academic discussion. Because as you and I know in assembly a goto (jump) is required. But in the extension of this discussion also was the “one return per function” something I break all the time these days.
@@gregorymorse8423 you may not have watched this video then. The goto must go idiom is obviously not correct there are use cases where goto is a superior solution. Hence the clean code is not objectively true. And I can prove it even further, as jumps are Gotos and are the backbone of branching on a machine level. So the whole “clean code” religion breaks down the lower you go. Just like the idiotic “one return per function idiom”. It makes code unnecessarily more complex and slower. Then the “though shalt not repeat thyself” which is something you generally should stand by, but I have had situations where unrolling code is faster. And when there’s one situation where your “religion” doesn’t apply you can’t not should preach it as a fact. And “spaghetti code” what is that?! Is it a jump? Because a call to a different function is also a jump and you need to grab the listing of that code and read it to understand it completely. Every program exists of different strands of “pasta”. Ironically you can get far more list in Uncle Bob’s OOP world then a piece of assembly. Because it’s hard to find a single entry in complex frameworks like spring, spring boot, J2EE that have scheduled or event beams or even C++ which spawns many threads or systems with event messages. So even “spaghetti code” isn’t an objective truth. And it’s also a skill level! I’ve been developing software for 40 years. I find that the last 20 years I find it far easier to reverse engineer code than I did it the first 20 years. Just because I’ve seen more. The first time I saw self modifying code, back in 1988 I was like: “wtf? I don’t understand what is happening here. Why is a memory location in code space updates?” I spend literally a whole weekend experimenting with it and realized it’s power. And how I could reduce the amount of code needed and speed up for example joystick routines on the C64. Since I’ve seen self modifying code a lot and it’s just a matter of checking to see what opcode is set and it’s clear. So yeah, even “spaghetti code” is busted as an objective scientific fact. Unlike ohms law that always is R=U/I or gravity is always 9.8M/sec and doesn’t deviate on Sunday’s or has an exception on Mount Fuji. That is an objective scientific proof. It’s merely an academic discussion and personal preference that are not scientific facts. Because there’s an exception on everything Uncle Bob and his predecessors preached.
Cool, might actually make use of this in work next week. I had the issue that my C-code was too large to flash it on a microcontroller, and one thing I recognized was that I had such error-checks where each succeeding one only adds a line or two. Might give it a go (to).
yea people who unilaterally dismiss a basic thing like goto as bad just make it harder and more confusing for new programmers to figure out how stuff works and what they should be doing. everything has a purpose, and most things have good and bad ways to use them.
For high-level language designers, thye must learn from examples where goto is essential in C and design features to avoid the need for goto, such as auto-dispose resources (RAII in C++ and Rust and using C#), or hand written state machines (async/yield).
The goto is literally not needed for any case whatsoever, in 20 years I couldn't fine any. Except the funny looking DuffyMachine, which is not even valid performance optimization anymore. Well, jumping from a statement to another in a switch would be a legal use for "goto", but you don't need it, just put the switch inside a for-loop and change the expression of the switch and do "continue". Hand written state machines can be created by mere function calls if you have a fix-point function to break the call-stack from becoming infinite. Its a common technique in Lisp. Maybe C programmers don't know that. Or maybe their compilers are too dumb to do RVO, aka, Return Value Optimization. Something akin to a ".tail" for that in its IR. (almost all virtual machines that are JIT compilers can do that, its kind of a trivial optimization)
@@monad_tcp it's not needed, but it can make your code prettier and less repetitive in some rare cases (the linux kernel often uses gotos for error paths)
@@int32_ yeah, that's a well know deficiency of the C language. that pattern is so used that compilers can actually do semantics analysis on it and I'm going to even admit that its the only valid use for goto. besides jumping to another case in a switch which is a neat misuse of goto that does work and very little people know you can actually do that.
Impossible. Goto is essential for representing irreducible CFG (for all procedural constructs are, by definition, reducible). Which means, pretty much any mildly complicated FSM, including all kinds of parsing and tree walking. Languages without a goto are crippled languages.
So in the "good" example for using "goto" I have this question: Why not simply make a method to which I pass the necessary parameters and then call close on them? What's the advantage of the goto in comparison? I might have missed it. But the duplicate code is not a problem to me as I can outsource any duplicate code into a method that gets the necessary parameters and just call that method instead.
Just now learning that goto and longjump are different. I always assumed the issue with goto was that it does what longjump does and lets you jump into a different function with the wrong stack setup
I would probably go with nested inversed if statements in a case like this, where instead of a cleanup on failure, you get a choice of continuation or local cleanup, which is then followed by the cleanup. Kind of like this: errors = 0 do buffer thing if (buffer succeeds) { do socket thing if (socket suceeds) { do socket use } else { perror("sucket") errors++ } close(socket) } else { perror("buffer") errors++ } free(buffer) return 0-errors
I remember in our first C# class the first task the teacher gave us was "make a loop". I proceeded to write a goto variant for it with a break i made, the teacher was impressed but was like "okay, but please do the other way as this one is illegal" xd. The reasoning explanation he had made was that unlike regular loops, this would make a randomized selection or something and that it would result in slower program in a sense.
I don't understand what your teacher meant to say, but you should absolutely not be using gotos in C#. If you have to use a goto in C#, it is telling me one thing about your code - your function is too big and needs to be split into multiple functions.
@@lycanthoss Lol, I just said the teacher also said it was suboptimal, but he was surprised someone even knew about it, because people usually learning languages like python nowadays, etc. don't really learn this stuff at first. It was back when i started learning and wanted to be a bit funny. Like yeah, it's not a good way, but a funny way nonetheless to make an infinite loop. I'd never use it under normal circumstances. After asking around the only place where i could find use for gotos is maybe in kernel programming or so.
As some people already pointed out, they are just another tool in the box, and i am thankful for having them on C#, they are very rarely the way to go but it can make things a lot more clear and simple when used properly, but normal programmers rarely have the need. For me i find it very useful for conditional retry logic and for exiting nested loops those cases are kinda rare. You could argue that my code sucks then, but in that case i would like to learn a better way to do those that still work without overcomplicating simple things
What your teacher was probably talking about is ASLR, which randomizes addresses. This is implemented to prevent malicious attacks that tells the program to jump to an arbitrary line of code. If the attacker cannot be sure of the exact address of their desired code, then they cannot reliably execute arbitrary code or steal data.
The reasoning sounds like hocus pocus to me, why you _really_ shouldn't use it is because it makes everything harder to reason about. You could use a goto to implement ifs, fors, whiles, and functions, but just using the keyword designated for that purpose frees you brain up from having to follow the goto chain to figure out which one it is. Freeing up those mental clock cycles makes it easier to figure out whether or not your code goes into an infinite loop or any other bad state _before_ running it and having to use the debugger to figure out WTF is going on.
Another good use case is if you're in a nested for loop and one of your inner loops needs to break out of the entire thing, instead of having a bool that propogates breaks up the loops, you can just have a goto that leaves the entire structure. I'm not sure, in c++, if this'll work with scoped variables, so that might be a case where you don't want to do it.
In this case, why would you not move all of those nested loops to their own function and simply return from it when you need to break all of the loops?
@@ChapmanWorldOnTube there might be a necessity to use a bunch pf local variables withion the structure, so you will need either to use global variables instead, that makes bug probability higher, or put all those stuff as a parameters into a function that is called from a single place of your code and depending on compiler that may cost you some perfomance and memory. Also the function will be stored at a different place in your code so you will actually need to spend extra attention to keep track of what is going on, so in this case a simple jump to a lable makes code actually cleaner and better in both readability and perfomance
@@WindLighter I of course don't share the same coding style or habits that you do, and I also don't code in C typically, so I hope you won't read it as confrontational that I disagree... Of course any call to a procedure/function is a performance concern, however, IMO making function calls is far more unfairly demonized than go-to. Function calling is a performance hit of a few milliseconds, which of course matters if called repeatedly in a loop - but presumably moving all of the nested loops within the isolated function means you need call it only once to initiate it - or at most, once per cycle of an outer-most loop. In terms of readability - yes, when reading the calling code you might have to go locate the function elsewhere in the source code - a small chore should you need to. However, if the function is well named, in many cases you may be able to read past it without being concerned. In addition, I find locating and reading separate functions to be far easier than the alternative - one long sprawling list of nested loops gives me shudders. I envisage functions growing to many pages (and have worked on such code bases), and would very much prefer to have to go find a function, rule it out in terms of relevance, and return to the calling function to continue reading. Parameter passing - If there are many local variables to pass then this could increase that function call overhead a little, but it also indicates a possible design flaw. Could the state information be wrapped up in a struct, which could be passed by reference? This isn't a cop-out, saying just pass a struct, but rather, all of those local variables must be logically relevant to each other (in abstract terms) to be required in the nested loops, and therefore could be conceptually combined into a struct representing the computational state, and with a suitable name to reflect what that is. From this the very concept of OOP falls forth. Ultimately, goto is an individual choice or preference, but I feel if you're going to use it, it should always be a prompt to reconsider, is your abstraction of the problem a good abstraction.
@@ChapmanWorldOnTube The problem with that solution is that you then must unpack the variables from the struct. Whether they're passed on the stack, and thus take up valuable stack space, or passed by address while contained in a struct, it either requires extra space to be used up or extra syntax to be had. Both solutions are less easily understood at a glance, more difficult to type in, and potentially fraught with extra bugs, on top of requiring extra copying of values which in a long-running loop will add up. Now, if the compiler optimizes said functions because you label them static and use -O3, you still have harder to grok code with more potential for bugs.
@@anon_y_mousse We are clearly coming from very different places. I think I hold the opposite position on every point that you made. I don't code C anyways, I have never really liked the syntax. I have always been more in the Writhian camp. I will bow out on a mutual disagreement on this one.
goto statements are a powerful advanced tool. They should not be used by inexperienced developers simply because they're all too easy to abuse and make an habit of it, which is what used to happen long time ago - hence the stigma. But like anything else, goto statements should be encouraged whenever they are the best tool for the job.
Interesting. For the example you used as a good usage for a GOTO, I think the step-of-error-handling functionality *could* be reproduced using a switch statement instead (by passing in a value representing which step you are on, and letting that decide the starting point). But I think then use of a goto would make it more readable. Still, I can't help but wonder if there's an even more elegant way of dealing with this kind of circumstance.
In this instance, while I love C and I still think it's the best language, C++ actually provides a more elegant solution whereby the cleanup code goes into the destructor for a class so that if any part fails you could simply return from the function and the appropriate cleanup would occur. Even though I borderline hate Zig for the syntax choices they've made, I'll admit that defer is also a rather nice option and slightly more flexible.
In more recent languages there is defer and sometimes errdefer which does essentially the same thing. In languages with lambdas and destructors you can synthesize defer by creating a type with a constructor that takes a lambda and a destructor that runs the lambda.
I remember in highschool the book had an entire page saying how goto is the black sheep of computer commands and mentioned that some guy wrote an entire book against the goto. I wasn't taught back then how to use it and I wasn't taught how to use in the electronic engineering tei either(we only learned visual basic and c# there)
They are good for state machines in particular. Tail call functions are sort of better versions of them, and the difference is informative. Tail calls need to have context explicitly passed, while gotos retain the same context. This is why goto is so dangerous. When you read a block entered via goto you have no idea of what the context is (neither does the compiler), but responsibility for cleaning up the context is passed anyway! We often see weird loop contortions in an attempt to make things that are not loops fit into them, and it really bugs me. State machines are some of the worst offenders, but maps are often maps in a programmer's head, converted to a loop for the code, then converted back to a map by the compiler for vectorisation! Goto is a fantastic pattern for building a state machine where each state is working on the same context, such as parsers. Sure, the control flow is unpredictable, and 'spaghetti like', but sometimes this is inherent to the system you are modelling rather than bad code. Goto is a very crude tool, but most languages don't have better ones for non looping control flow. Tail calls are pretty close, but ideally you would want to avoid the function call overhead when you just want to build a state machine. I would like to see dedicated state machine control flow structures, more restricted than goto, but still able to handle non looping control flows elegantly without programmers having to translate it out of the form that it exists in their head.
The only go-to I ever used was in assembly. A manual step maker for a microcontroller that had to run regular checks for a critical value and therefore use a binary pointer with a bunch of gotos to do a bit of work and then return to the listen check.
I have a really good example. Imagine you have some loops, on inside another, and if a certain condition is reached, you want to break the first loop (or continue), in Java you have loop labels that allows you to use continue labelName; or break labelName; but in C++ we will have to use a goto instead. I think is a really useful use case
Some asshole wrote a paper titled "goto considered harmful" and 56 years later idiots are misinterpreting it having only every heard the title and not the body and taking it as a universal law completely removed from it's original context. The paper was written in a time where FUNCTIONS were considered some confusing new-fangled invention by many programmers. The goal of the paper was to convince people that you should use higher level abstractions like functions. If you as a programmer in 2024 use functions (YOU DO) then you can COMPLETELY DISREGUARD "GOTO considered harmful" because IT'S NOT FOR YOU!
back in the day FORTAN was widely used - and it had 'computed goto' and 'arithmetic if' which could easily create messy code which is nowadays completey inconceivable
Aside from getting out of nested loops, I will use goto for cleanup upon some error being detected when I allocated memory and there are multiple possible points of failure afterwards, as you illistrated in the video. I think this completely reasonable, and no amount of "goto is bad" will ever change my mind. This is essentially what "defer" and "errdefer" are doing in more modern languages.
I've only ever used goto in C where I would've used "break 2;" (or any larger number) in PHP. Honestly, I'm 50/50 on which is more readable: "break ;" is more readable in an immediate sense, but "goto breaknum;" is more readable when you want to figure out where things are going, especially if there's a complex nesting going on. Either way, it's better than the JS version, which makes you put the label _above_ the scope, then "break label;": i.e. "forlabel: for () { if () { break forlabel; } }" I get it, you're labeling the scope and then breaking from that scope, but it's still the worst of all worlds.
The point I made a half century OK, (OK, more than) is that you want to reading the static state of the code to give you an understanding of the dynamic state. Unrestrained gotos make this difficult. This is why we use words like "try/finally", "break", "continue" etc. to indicate that are doing constrained flows and thus our reading of the static code is consistent with the dynamic flow. If you're using an ancient language and need to use GOTO, then annotate the usage to give the reader (and yourself) and even break (pun?) in understanding what is going on.
We are not allowed to use goto at work, combine that with the fact that we are using C++ 17 (much of it much older) … yeah, our code is completely unreadable. I loathe having to write messy code just because I'm not allowed to use goto. At least some code is Java, and where you have labelled breaks, so it is a little better…
Grew up typing in BASIC programs on my Commodore 64, and GOTO was very common. When I learned C in college, the course didn't even mention the keyword goto. It was years later that I learned that goto was recognized in C. I can say the ONLY time I ever used goto in C was when I was trying to debug something and used it to skip over a section of code.
BBC BASIC allowed computed GOTO/GOSUB, which I made good use of to replace long ON n GOSUB .... with GOSUB 10000+100*n I also made use of computed RESTORE to save memory (Avoid reading ALL data into an array) with a simple RESTORE 10000+100*n The C compiler will compile some switch statements into computed branch if the cases fit a pattern.
@@Lord-Sméagol and when you have a direct access to the computed goto, you can pre-cache the destination labels, while a switch will introduce a level of dereferencing with a label table. See how threaded bytecode interpreters use computed goto for example.
Another case I can think of is breaking out of a nested loop without introducing a one-shot flag variable, or likewise continuing the outer loop while inside the inner loop
I'm working with an SDK of a transaction processing system which uses callbacks to send events. The callback function is essentially a big switch/case statement processing the event the SDK sends. There's one case where I need to process a failed transaction which is executing a function and closing the transaction exactly as the successful case. I've been contemplating using a goto there for maintainability. There's a strong chance the successful transaction case will change in the future and might not get updated the same way in the failure case; the goto would make sure the code is only written once. However, using a goto in a switch/case is quite an anti-pattern.
the way i have experienced is that we tell it to newbies because newbies don't know how to write code and we don't want them to use it because they will be confused and write worse code. i have seen this live many times, while helping in uni.
I think there are some cases where you have an ultra fast loop or function whereby inserting a goto can save on t states. The jump instruction is really fast, plus also consider what variables are in the registers as this can make a big difference.
This is the first time i've ever I just instantly went and bought a course from a UA-camr as soon as you mentioned the C course. I fell in love with C after years and years of using higher level languages but learning materials were a bit sparse so I'm super excited for this even though I'm not a beginner in C, I'll learn a lot I'm sure! :)
Hey man, this is really cool. I wanted to compare the code before and after the goto statements. I wanted to check if a function call with a little refactoring wouldn't be an even better improvement, completely dropping the goto. A link to a gist or something would have been incredibly helpful for that.
Bad example to be honest. This case can easily be substituted by simply nesting the code. Or, if you don't like deep nesting, by function calls. I.e. int proceed_with_sfd(int fd, void *filebuf, int sfd) { // Do stuff with fd, filebuf and sfd return 0; } int proceed_with_filebuf(int fd, void *filebuf) { int sfd = socket(...); if (sfd == -1) return -1; int retval = proceed_with_sfd(fd, filebuf, sfd); close(sfd); return retval; } int proceed_with_fd(int fd) { void *filebuf = malloc(...); if (filebuf == NULL) return -1; int retval = proceed_with_filebuf(fd, filebuf); free(filebuf); return retval; } int main() { int fd = open(...); if (fd == -1) return -1; int retval = proceed_with_fd(fd); close(fd); return retval; }
@@malcolmgruber8165 because this is 1000 times more readable and easier to debug. Following your logic lets write all the code in 1 giant function with goto jumps. Not to mention that such small functions will be inlined by the compiler anyway. So that kind of premature optimization quickly becames pointless.
@@mk72v2oq Honestly, I find this a lot less readable. Now you have 4 functions for a trivial thing, and if you do this with all functions, you end up with a heck of a lot functions, all of which require a sensible and unique name. To follow the control flow I now have to scroll upwards and scan function names, which are going to get progressively worse the more you apply this, and keep track of what each argument actually is. It requires more code that you not only have to write, but also have to parse when reading it. And either way, you just have a single linear control flow with multiple entries to a single linear flow on failure. Except that in this case, both linear flows are scattered in the code.
@@jbird4478 well, it is the nature of C. In practice you usually can do it smarter and shorter though. The main goal here is to restrict area of responsibility of every function. The problem with goto that it can be misused very easily. It is even more dangerous than regular unsafe C things. But yeah, the language itself is inherently flawed in this regard. C is the last one where goto is even thinkable (if you don't still code in BASIC lol). Embrace modern languages if you can. Functional elements make it way more clean. Also, when control flow has conditional branches, it is not linear by definition.
@@mk72v2oq Any programming construct can be misused. It's the nature of C that you can use goto for such situations. And any paradigm can be over implemented. Limiting the responsibility of functions is generally a good idea, but it can certainly be overdone. To open a file, in practice one often has to do a few things more than just calling open(), but that doesn't necessarily mean they all have be separate functions. It can be practical to keep a reasonable amount of things in a single function. And you're right of course. I should've put "linear" in quotation marks or something, because what I meant is that if all goes well there's really only one code path. And if you're reading it, that's generally what you want to know first. Then the points of failure are things you follow if needed, but in case that's also just a single path. So regardless of how you write it, what happens here is the code runs from A-Z, except it jumps out on failure. And that's precisely what the version with gotos reflect in the code. Higher languages provide other alternatives for this, like exceptions or scoped destructors, but C doesn't. C provides its own tools, and one of them is goto. Like all tools, there are situations to use it, and situations where you should not, but there's no reason to avoid it at all costs like some people seem to think.
I started programming in TI basic and got accustomed to using 'goto's just to realize that they are either not in or frowned upon in other languages. The irony is that asm is full of 'goto's in the form of 'jmp'.
I fully agree! This is great code. I do not write C too often, but I have been correcting some of my own code similarly to this. Albeit with a break statement in my case. The point is to not write any more resource deallocation lines than allocation lines, as a good practice. In order not to accidentally leak resources or create duplicate frees. As soon as you allocate stuff, you do not want to diverge. All code paths should come together at the end of your code. It's good te remember that C is a low level language and does not have any try finally constructs or RAII features. And there's no GC. It's not a functional programming language where you can always do lots of return statements without any worries.
Just make 3 cleanup functions that each call the next step below, if there is one, so if you fail early, you shortcut the stack, but if you fail late, you call the whole stack
The very few times when I used GOTO in my C code was always for the reason shown in this video, to avoid duplication of a clean-up code before returning from the function.
maybe it's a silly question, but why not add and call functions appropriately for subsequent cleaning steps? You'll still have clean code (and in my opinion even cleaner).... Maybe the issue is a bit slower performance due to pushing parameters on the stack etc., but I don't see any other disadvantages... and in nowadays we have so powerful processors that this shouldn't be a problem
I remember even in late Visual Basic programming, there were sometimes esoteric cases where I actually resorted to those old GOTO/GOSUB statements. For example, VB doesn't have a "continue" feature inside loops, so if I needed to do that I'd label the end of the loop (e.g. "ContinueHere:") and GOTO that point.
@@Hublium Yup, I remember using that. Especially when dealing with file I/O. A bit more dangerous was the "On Error Resume Next" which was super convenient if you needed to test just one call that might error out, but you had to remember to turn it off again (On Error Goto 0) or you've just suppressed all runtime errors globally until program execution terminates, potentially opening up all kinds of glitchy misbehavior downstream. Have fun debugging THAT!
An exception in C++ is basically a goto chain. Everywhere, where throwing an exception in a higher language would be appropriate I consider a valid use of goto. Something resembling try-catch-finally would be an unreadable mess in C without goto
I did exactly this for my college project, because using early returns seemed more messy. I realized that 'goto' is not some forbidden practice and this is a valid use-case. Like with breaking out from an inner loop. Is this a normal practice to do with C in a professional environment?
I’ve never seen goto/raw jumps as illegal in whatever language I was using that had them, I just considered the results of their usage to be more _potentially_ uncontrollable or unpredictable and therefore requiring more discipline, foresight/planning, and intention in code than “standard” conditional logic conventions.
Also if you're using C++, the compiler cleans up if you use goto to leave an inner scope just like with other control flow statements, but it may leave variables in an ill-formed state if you go past their declaration.
but it's true that the labelled-block-break mechanism of java can almost always do the same thing, and sometimes it's clearer to have the 'section that can be jumped out of' in a block like that.
I made a DFS klotski solver, and using a normal recursive function it gave me segmentation fault, but doing the recursive parts with goto worked flawlessly
I remember our high school computer programming teacher in the mid to late 90s didn't even want us talking about the goto statement. It was a bit of a joke. Growing up in the 80s, home computers like the Color Computer just didn't have the fancy loops and structures of while or case. Except for for/next, we only had goto for jumps.
In C# your example would be: using (open file) { do something } that's all. If "do something" fails it will close object that was created in using section - in this example close a file. Also similar think you can make with try catch section. You can use functions with return. Also using break and continue. The only reason to use goto is when you want break from double loop. (Loop in a look). Break will exit from one loop only.
I usually only use goto in command line apps where I need to validate inputs. Invalid input? Move back to the line that takes the input. Super simple and readable
When I use goto, it’s only used for the resource cleanup and error code return when something wrong happens in the middle of the function. It really helps me keep away from nested and duplicated code. I can’t think of any other good usage of the goto keyword.
The biggest problem to my knowledge with goto is the potential to mess up the stack. It’s generally just safer to build off the stack with a series of function calls than create a state where the call stack is uncertain
The main use I have for goto is breaking out of nested loops. Yes, it's possible to do it without goto, but most methods for that make the code less readable to me. People only really do that for the sake of not using goto.
We have it so good and rough these days. IF and GOTO were the only flow controls we had. There was no malloc or garbage collection. We had to remember the addresses of the ROM and RAM usable memory registers and read/write them directly with PEEK and POKE. You could make a sheet of graph paper into a chart and memory map the entire program and map the execution flow end to end before typing anything if you wanted. What's old is new I guess. We started with strict typed languages and mainframes, then we wanted dynamically typed user-friendly languages and our own servers in the enterprise, now we are back on the strict typed language kick and cloud computing as a modern replacement for the mainframe.
Just pointing out a little error in the first version of the error handling code. You should call perror first before calling any function that could set errno. In this case the error message won't be very helpful since it will print the termination message of the close function instead of the malloc error. Great video 😁
Pre-emptive guess: Because humans make mistakes, and Gotos divorce syntax from the underlying logic, making mistakes more likely (e.g. accidentally reusing a Goto label, using the wrong label, reusing a variable from another part of the code)
That last case you mentioned (the good use of a goto) feels like a switch statement in a way. I supposed as most languages I use don't have goto, I'd increment or set a numeric variable and then switch() it for error handling. Great video! I learned a lot :)
@@JodyBruchon you obviously are extremely ignorant. Switch statements are jump tables except for ones with only 2 or maybe 3 branches. You literally have no idea of how modern processors and compilers work. You've given serious evidence.
@JodyBruchon maybe I'm being a bit harsh but seriously Wikipedia "branch table". Practically speaking this knowledge is important though with a good compiler perhaps it needn't be.
Goto has a place, here's my easy advice, if I don't have to use them, if there is a more logical readable way, I'll use it, The compiler can handle making the goto statements, which just become a jmp in assembly anyway. A great example of the compiler using a jump instruction to great effect, take C#, make a switch case statement. If it has more than a few elements, and they are sequential (if your cases aren't make your cases into an enum, so it's sequential) it makes a jump table. Which is.... goto values under the hood, However, this means that the resulting code is easier to follow the execution flow. In oop, goto is generally frowned upon. Even though under the hood the IL and the assembly is using it to great effect. If you're writing procedural code? or following functional programming workflows? They can be incredibly useful. Are you getting low level, programming for bash or batch files? The scripting you'll need to use goto: labels. Basically. It comes down to situation. Most modern languages we use them extensively.. we've just abstracted them away into inbuild functions that handle all the madness of jumps and pointers for us. Lower level, we've abstracted them to "Here take this pointer, do the thing at that location" aka... methods, subroutines, functions, whatever you want to call them. Then lowest level if this then goto: It's not that they are evil, or illegal, it's just most languages we've evolved the need for them out, the compiler handles all of that for you, so if it's not a weird edge case, you shouldn't need them. However edge cases and older languages exist, and sometimes, in the name of performance, those layers of abstraction are an obstacle and you just have to get low level and handle that stuff.
Could've initialized retval to -1 to save a line per error case. It also would've implied that you don't succeed until you do. Also, if you've ever read a reasonably complex Batch script in Windows/DOS, you'd know that gotos were the closest thing to functions unless you enabled extensions. Cascading goto error handling was also commonplace.
Use of "goto" being good or bad also depends somewhat on the language. For instance, this error handling case is demonstrated in C, but in a language which supports try..finally (such as C# or modern Pascal variants), it would be at least more readable, if not more elegant, to dispose of invalid resources in a finally block, having already logged the error state. This is even more true in the (often very misunderstood) modern Pascal case, because pascal allows you to set the return value of a function without returning from it. The real problem with "goto" is that it's akin to a break / return, but without a boundary (the outer loop, function, method). This makes "goto" very easy to abuse in terms of code readability. Readability is of course somewhat subjective also, and so my argument generally falls to this: I've been programming various languages for almost 30 years commercially, and longer as a hobby, and I've never yet found myself in a situation in which "goto" is absolutely necessary - there's always another way to solve the problem. That being the case, if there's potential for it to upset the readability of control flow - it's probably best to simply avoid it. The counter argument, I understand, is that ultimately all code boils down jumps (essentially "goto") at the assembler / machine code level. I feel this argument misses the point of being a good citizen with regards to readability at the higher level. I don't think anyone could argue that "goto" can't be used to create rats-nest code, so if you could convince me that there's a case in which "goto" is the only solution, I might well reverse my position on it - but for now I remain firmly in the "goto" == bad camp.
Even simply having exceptions opens another can of worms, especially the exceptions + destructors combination. They solve one problem, while introducing another one, and now your program has to maintain a lot of runtime information internally, just for a tiny bit of convenience for programmer. Just having gotos isn't as ugly in comparison.
In strict terms, no one path is necessary as you can rework the code to your heart's content and make it amazingly convoluted and difficult to debug just by being an ideological drone that spouts nonsense like "goto is evil". That doesn't mean it's the best decision, and possibly not even a correct one, but you can do that if you like to leave the world a worse place than you entered it just for ideology. Or, and I'm just throwing this out there, you can use goto in places where it not only streamlines the code but improves the clarity of it and try to teach those that follow you the correct usage of the construct instead of telling them to use a worse solution.
@@anon_y_mousse Calling it an ideology is a strawman argument. This debate has been going on since the 60's, perhaps even earlier. I gave reasoning behind my position, that is not ideology. This would be akin to me telling you that you are only in favor of goto because you have an inferiority complex - I am sure this argument would be unfair, you likely have quite different experiences that lead to your position.
@@ChapmanWorldOnTube No, it's an ideology because all the arguments against using goto are invalid. Essentially it all boils down to saying you don't like the way a certain method of flow control looks and thus you hate on it and generally anyone who uses it. If you use any imperative programming language then hating goto is nonsensical. The only way to escape having the look and feel of goto is to use a declarative language, and even then it's still only thinly veiled due to the way a computer operates.
@@anon_y_mousse And this is another straw man. To paraphrase you "All arguments against goto are invalid because it boils down to you saying you don't like the flow control implications..." This is not true. The arguments against goto aren't merely opinion. Edgar Dijkstra's "Go To Statement Considered Harmful" letter didn't say "I don't like it therefore it's bad" - Instead it carefully explained that there was a measurable drop in quality in software that was proportional to the number of times that goto is used. It then goes on to reason about why this is, with the hypothetical conclusion that programmers, being human, had increasingly diminishing capability to retain the context of control flow the more goto is used. This reasoning was followed up on in much research since, and found to be valid. Goto being bad is not about a coder looking at the text and thinking it looks ugly, nor is it about following some dogmatic idea that some authority says its bad so it must be. Instead, goto being bad is based on two issues. First that it allows the programmer to violate the closure of a function/method as a feature, and second that it makes code objectively (not subjectively) more complex and thus more difficult to understand. Side note: When someone says that code is less "readable", what this *should* mean is less "understandable". Many make the mistake of thinking that readability is an aesthetic thing, like the decision between putting spaces inside of parenthesis or not, or to use tabs vs spaces. Such things are a style preference and not really relevant today. In modern times we've all read other peoples code online for one reason or another, and if we don't like the styling we can simply change it. Instead, what makes code "readable" is how easy it is to understand. Developers often pride themselves on how clever they are, but rarely consider if their code is understandable to others. A rats-nest of goto is, again objectively rather than subjectively, less easy to understand.
wanna get good at programming? check out lowlevel.academy and use code THREADS20 for 20% off lifetime access. or dont. im not a cop
What standard of C do you use in this course? I hope it's ANSI c89. I really enjoy your videos, you are one of the persons that inspired me to learn C. And the project you've picked is just brilliant. I just wish to finish Herbert Schildt C++ Reference (only C part) and Dennis Ritchie C programming language books before starting your course, I hope it makes sense
There is an other way where using goto is more readable. Make break with multiple loop. Without goto, we have to define an exit variable, and the test statement of the loop looks unreadable.
There are loops which are impossible without goto.
I've been programming for over 50 years and goto has a definite place in the toolbox. Now for highly structured languages such as C/C++, Java, etc. goto can be somewhat confusing. But for procedural languages such as cobol (yes, cobol is still used today), Fortran, etc. goto can actually make the code more readable and, moreover, more efficient. For assembly language, goto is a necessity.
Edsger Dijkstra probably hates you. Just like BASIC
It annoys me when people group C and C++ together as though they're the same language. It was already easy to make a program that was valid in one but invalid in the other by C99, but now it's nearly impossible to not hit that wall. Java kind of pisses me off due to how little foresight they had in designing it. Not merely removing goto but making it a reserved keyword that generates an error at compile time, and then they had to add functionality to break and continue just to make up for the deficiency which caused a lot of problems that C++ already solved but in a more elegant way while still allowing you to use goto for those problems where it was still somewhat necessary. I guess what I'm driving at is that these ideological stances that people take based on inexperienced programmers misusing tools leads to garbage language design as well as garbage program design and I wish the lies that perpetuate them would stop cropping up. The old Knuth quote about premature optimization really irks me too.
@@bb-sky But it *is* telling it to continue, from the top of the loop. Though, I wouldn't be opposed to using next as the keyword to start the next iteration of a for loop, it wouldn't make sense in the context of a while or do/while loop, and in the interest of lowering the keyword count I'd delete the one that's the odd man out.
@@bb-sky Interesting perspective. But you'll have to come up with a better word than next or skip to convince me. I can't think of a word that's more apt than continue, only phrases.
@@anon_y_mousse restart?
"GOTOs make your code unreadable, therefore code without GOTOs is readable" is the "denying the antecedent" fallacy.
Also annoyed me a bit
Nobody accused him of being educated
If that was his whole point then sure, but it was just an introductory little shower thought
Woosh
@@james-m-8285yes you are very smart buddy
You can also write a function that does this for you and call that function with different parameters if you are scared of the unstoppable errors that might come with this approach.
This can lead to some ugly bugs by getting the file pointer on another scope
Function calls add quite a bit of overhead compared to goto statements, unless they're inlined (which from my experience doesn't happen very often)
@@talkysassisuse rust if you want to be safe and 0.001 ms wont make a difference, for some embedded systems you have limited size and memory.
Splitting the logic of the function with it's error path in another function tends to make things much messier and make easier bug as you need to sync the error with your function in any case.
You also might pass a crazy amount of params of your function to your error function !
but with a function you can't break, continue, or return from the function that it was called from
Yeah agree. The error handling case mentioned here and sometimes breaking multi-layers of for loops is where goto make more sense than normal control flow. They are literally everywhere even in source code of linux kernel.
Or exiting a loop in a switch.
Their use is recommended in the kernel to support a “centralized control flow” method of coding
Its because normal control flow is lacking in the C language. Something like "try/finally" , I don't know.
@@monad_tcp try/finally is not normal control flow but exception handling, which requires quite a lot of runtime support. C is just too low level for that. That's not to say you can't implement it in C, but it's not part of the standard language because that is designed to be portable across a huge amount of different systems. Try/finally requires stack unwinding, which is very system specific. With gcc, exception handling for C++ is actually implemented in C. Under the hood of course, all control flow eventually boils down to a form of 'goto'.
@@jbird4478 try/catch/finally is control flow.
It changes the point where the execution of code is going, therefore it is a control flow statement, compared with all the other nodes in the AST that are considered expressions, not statements.
I'm using a formal definition here. (not the C programming language specification, but more general)
You might correctly argue that C is low level for that sort of control flow, you might be right, C is a "mere" macro-assembler.
On the other hand, even function calls might be considered special "control flow" as the C programming language does run in hardware without a stack.
All of that is merely an implementation detail.
But you do have a point.
I've only had to use a goto once in ten years, to escape a highly-nested for loop. I could have "unrolled" the for loop and turned it into 5 functions, but I tried that, and it resulted in a code that was less readable than with a simple goto. My boss fought me for it, but in the end, dogmas and tradition matter less than producing code our teams can actually use and maintain easily.
couldn't you have used a labeled break?
@@dracheflydepends on the language, some languages support specifying now many loops to break, which is error prone, and/or some have labeled breaks, while others don't. I would say goto is essentially a labeled break, since this is the only real use I see in it in 99.9% of cases. It is still clean because the jump is directly after the loop to be broken, so you don't make spaghetti like this video made.
@@drachefly or put the loop in an extra function and leave both the loop and the function with a 'return'.
@@amigalemming That'd work too!
@@amigalemming Exactly what I had in mind.
Another use of goto is for breaking out of a nested loop.
Imo discussing why GOTO is shunned without talking about what it actually was when "GOTO considered harmful" was written does the discussion a misservice.
So, GOTO at the time a lot more powerful than what C is able to do. It was not only in most languages the primary way of handling control flow, it was also able to jump across function boundaries. Here an example (in C syntax because reasons):
void f(int i) {
label:
print(i);
}
void g() {
goto label;
}
This lead to the problem that you couldn't think of function like a black box. You needed to know what it does if you want to reason about your code. And because EVERY function was able to do that, you needed to practically know the contents of EVERY function.
And guess what, stuff like this was so widely done, that at the time "GOTO considered harmful" was published it was highly controversial ("You want to say I am not capable of doing this safely?").
That's good to know, I didn't consider it used to work like this. I was always under the impression that the label has to be in scope, so it's not easy to abuse it. Maybe in older versions of C and compilers without following the standards. And the article would make sense then if people did this regularly. I don't even know how that would work, because if in your example g() jumped to label in f(int i), then what the hell is the value of i at that point? Why would anyone do that even if allowed?
But thinking about, we have much worse things nowadays that will actually break the flow, something inside scope affecting things outside scope even without you realizing it.
I can't imagine how the code snippet should do other than undefined behavior
@@xwtek3505 as I said, in C it would not even compile
in the languages back then? well, UB wasn't really termed well yet and everything not otherwise defined was "implementation defined behaviour"; so, yeah, it worked because programmers looked at what the implementation actually did, and then used that to their advantage
not with undefined behaviour, that's not really possible anymore since what is being done under the hood is more often than not unpredictable
I would argue that we are worse off know in that department
nonetheless, goto thankfully can't jump outside of functions anymore
I think scope restrictions are a major part of why GOTO got deprecated to begin with. Even at the lowest level, if you GOTO a code point that's inside a different function scope, then it encounters a RETURN (without having first called said function in the usual manner) the return address it pops from the stack may be anything from unpredictable to actual garbage -- likewise, references to local variables pushed onto the stack may return unpredictable/garbage values.
Bro u need goto ur gonna miss out on 0.1us
In C, handling errors in resource acquire/release is already error prone, and goto specifically for this case and only for forward jumps reduces human error. This is a common pattern in kernel code itself. If you're using C++, you'd be doing it wrong by using goto since we've got Resource Acquisition Is Initialization there.
Agreed.
Did you study electrical engineering metal? I didn't expect you here I like your videos
what's not error prone in the C language of making errors?
Oh, I should have more respect. I meant.
Fastly making errors as fast was humanly possible.
yeah c++ has destructor and unique pointers so you don't need to use the goto clean up pattern
@@gustavoholo1007 my day job for the past decade has been as a professional software engineer. Studied computer engineering in college and grad school with a focus on computer architecture, but didn't get along well with hardware design, so here I am as a programmer instead
Looking at C code where you check the return type to see if it is an error makes me so thankful that Rust has Option, and Result enums.
What advantage is that? You still have to check if it errors. Plus, you could make your own Result type from an inlined tagged union
@@kevinyonan9666You only have one way to check for error in rust, instead of checking for `NULL`, `-1`, `VALUE_ERR`, or whatnot, and then reading the `errno` or whatever mechanism is used to know what the error was. In rust you just add a `?` or you use one of the error handling methods provided by Option and Result - with most of them sharing the same name across the two types.
Agreed, can't live without them.
@@kevinyonan9666 The compiler warns you when you haven't taken care of a Result enum, and it also can only represent Ok() or Err() as its variants.
@@kevinyonan9666you are FORCED to handle the errors or else the compiler refuses to compile your code.
Finally someone (else) that shows evidence where goto's are useful. Folks, goto's aren't evil; they're just another tool in the toolbox to use when it makes sense given all of the factors. (Singletons in other languages are similar in this way.) Having said this, part of determining "when it makes sense" is to not take it too far and abuse it.
People want shorthands that let them avoid thinking. Every tool is an asset, and you should have better reasons to use them or not than "someone told me so".
*"not to take it too far and abuse it"*
I've been having some discussions with some Python programmers and they've been suggesting doing things like using the exception system for things other than raising and handling exceptions. It was a good reminder as to why things like GOTO are considered bad practice - if you metaphorically give some programmers a gun they will eventually shoot themselves.
That said, GOTO in the event of an unhandleable exception in C is probably itself an acceptable 'exception' given the lack of a formal mechanism.
@@loc4725if you give anyone a gun* there
@@loc4725 Using exceptions for other things is very common in Python. You'll often see usage of 'KeyError' for example when something not being in a dictionary is just a normal expected state. I don't like it (but I don't like most of Python), but it's not a reason to ban exceptions from Python. Likewise, misuse of the goto statement is not a reason to ban it. You should just accept that some programmers will shoot themselves in the foot. There's no such thing as a good and also idiot-proof programming language.
@@jbird4478 There do appear to be quite a few, I guess you could call them 'mistakes' in Python. Looking at it's development history is like watching people learn what makes a language good / bad but with little knowledge of what came before and why certain features were dropped, added or made that way.
Also I wouldn't "ban" exceptions and neither did I say that was a good idea. My problem stems from experience of how certain language features or paradigms end up being a problem because you cannot trust many programmers not to abuse them. And yes I agree, all you can do is limit the damage but then that is what a good high-level programing language should aspire to do. It shouldn't needlessly place scalpels on the dinner table or worse, force people to use them because there's no other choice.
Goto is fine, just don't go up.
You're 100% right .. Read any major projects that power major infrastructure (all C... Hmm) ... You'll see lots of goto, always down to an error condition. It's a pretty common trope in C. Nothing wrong with it.
@@MrAB-fo7zk Not *always* down to an error condition, sometimes you just want to skip over a block of code because a condition is met and it's cleaner than nesting that block within a large if clause.
Bottom line though is that as long as you are using it to move down from where you are at the same level of brackets, you are absolutely fine. It only gets sketchy when you start using it to go up (IE creating a loop) or to the right (IE you are jumping into a condition or a loop). That's where people start getting themselves into trouble.
@@deedoubs you're right, I meant to say just "usually" for an error condition, not always or that it must be for that purpose!
The important thing is to maintain a consistent, traceable flow of execution. You can make a real mess of spaghetti code using goto.
@@MrAB-fo7zk That is because a GOTO is a excellent way to abruptly escape out of a function or even the whole program when a serious error occurs.
Java does something similar with Unchecked Exceptions (aka a runtime exception), which the programmer is not required to handle.
A very common example to come across is the NullPointerException, which usually means that a reference which was expected to point to a valid object is /null/.
E.g. It's like a restaurant receiving a takeout order without any customer information or which either contains items that aren't on the menu or none at all. --- I.e. there's little nothing you can do with it besides discard it
My favorite use for goto in *C++* is to mimick the for-else pattern from Python. For the unaware, else statements on for loops will only execute if the loop concluded by its condition, rather than a break. With iterator-based for loops, it's as simple as moving the condition inside the loop body and turning it into a break (that way, you can add additional code to that specific exit path). For range-based for loops, that is not an option. Instead, the solution is to replace all breaks with gotos which skip past a block of code immediately following the for loop's scope.
for (const auto& v : container)
{
if (some condition)
goto LOOP_EXIT;
SomeFunction(v);
}
std::println("All good!");
LOOP_EXIT:
...
Nice concept. Like when you have a piece of puzzle that almost fits and then you find the real piece.
Oh wow, didnt know that about for loops. Great to know
you could also do the following and skip using goto, although goto is cool.
uint16_t i = 0;
while (i < loop_range && !(some condition))
{
SomeFunction(i);
}
//destroy i
@@PalladinPokerI don't think that code does anything like what I wrote, but I think you were trying to suggest using the loop counter from outside the while loop's scope to detect if it terminated before reaching the loop_range? GOTO doesn't require an if statement to check the loop counter after the loop concludes, so GOTO is still better if that's what you're trying to suggest.
@@PalladinPoker That's WAY more inefficient due to the additional call and check, even more so if the outside function isn't inlined.
Don't be afraid of gotos. At the core, the goto statement represents one of the most fundamental building blocks of computing; the jump. Without jumps there would not be touring completeness. I love the fact that C allows you to directly use these very low level bricks, as you can implement control flows that are impossible with ifs, switches, whiles and for loops. Once again, C gets you closer to your hardware and that's a feature, not a bug.
All you need for Turing completeness is if-statement and while loop (both if these use goto under the hood). In a small number of scenarios, goto might make your control flow simpler, but you can always achieve the same thing without it.
@@lukekurlandski7653I think you also need either goto or recursion for turing completness.
@@lukekurlandski7653 Yes! That's almost right! You don't actually need while loops, just if-statement and goto-statement actually. In languages like BASIC you don't get a built in loop statement, you just use ifs and gotos to go back en repeat the loop until a variable reaches a certain value. I kind of like that because it reflects how your computer actually execute your loops under the hood.
Its stupid that wasm was designed without a jump.
@@roberthickman4092 br and br_if
I was writing a parser, and I used goto because I wanted to break out of the switch statement inside a for loop. It was an elegant solution.
Another way to do this would just be to put it in a function and return out
@@georgehelyar Yes, but there's also no reason to pollute your interface with more functions if there is only one user of said function. One of the reasons why goto has become this hated thing is because goto was the cool thing to do at one point, which just sucked ass when done on a larger scale and very long jumps. Doing it to break out of nested loops or similar is totally fine.
@@CottidaeSEAwhy would an additional function necessarily be part of the interface?
@@js46644 How would it not be? If you suggest making the visibility private, reconsider that thought and think about what an interface is. I do not mean the keyword interface you're probably thinking of.
@@js46644 I agree with you, there is no reason to make the function part of the interface. It should just be a static free function, a private static member function, or not exported from a module.
Man you are the sole reason why I fell in love with assembly, couldn't thank you more.
In general as a rule of thumb, I try to keep GOTOs in the same function scope, so just for loops and the like, it helps keep GOTO reasonably easy to read (and far safer!) but also still gives it a lot of flexibility. Also if I'm writing optimized code, I try to keep branches in general (not specifically GOTOs, they just tend to get messier), to be short and minimal, only things like small if statements, just to avoid the LOADS of cache misses you can cause by putting everything in their own function.
Note that in C, goto cannot be used to jump into a different function (mostly because it would be unclear what should happen when that function tries to return). You can use longjmp to do that, but only if the function is already on the call stack (i.e. it behaves a little bit like throwing an exception, but without proper language support).
My advice is to avoid using backward gotos (prefer writing a loop), and to only use forward gotos to eliminate excessive nesting of if statements or duplicate code. Hand-constructed goto loops can be very difficult to understand, and simple if statements are usually more readable than if-goto compounds.
goto has one really important use for me that aligns with this- it's one of the only ways to short circuit out of a large nested loop algorithm without having to explicitly end each loop.
Is there a language that allows goto to a label in different function scope?
@@xwtek3505 c++ (at least the mingw compiler from experience, but I think any flavor) can do goto from any point to any other point and can cross scope. it is almost always undefined behavior to goto from one scope to another. notable
examples of where it can be used is as a return from a void function to the main scope, calling into a function that returns void and uses no arguments, but you have to goto back out, and, it can be used to navigate self altering code if you have the balls to write it.
I will use goto to leave nested loops if there's no good way and I'm relying on local variables heavily in the destination rather than refactoring it into a referencing function.
c++, and c, will let you do all kinds of crazy bs. almost every combination of characters that compiles will result in a crashing program. it takes very specific sets of characters to result in a stable execution.
there are many things you can code in c that will result in an undefined behavior, but a repeatabe result, and that gets used for what it actually does, whatever that is. if you want a Lang that doesn't spit out an executable if it has undefined behavior- go use rust and run into compiler walls
@@xwtek3505 yes, c, c++ both allow you to goto between scopes. it's practically always undefined behavior. notable exceptions are as quasi function calls but you need to save the origin, and self altering code, if you have the coconuts
--at least the mingw compiler will allow you to compile with these using certain flags.
I always love this argument. Writing high level code without GOTOs creates more readable code. This is mostly true. The funny part is, the compiler generates tons of jumps (gotos) in the final binary, the actual assembly language. Just disassemble a switch statement, or if-then-else. JMP (goto) is there.
just because the compiler does it for you doesn't make it a good practice. Source code is meant to be readable, so make sure it is. If it wasn't we would all be programming in binary. Because that's what the compiler ultimately makes of it.
@@iWhacko @JodyBruchon
I think you replier's are missing my point. I've written code with high-level, mid-level, and low level languages for 35 years on a variety of embedded platforms and various computer systems and have always followed the standards of well written, highly commented code as every programmer should, so I will repeat this one little part again. I still find it funny that so much emphasis is put on NOT using gotos in code, even though the compiler has NO choice but to put jumps/gotos into the code at the BINARY level to make things actually work. So, at the lowest level, this no goto philosophy is bunk. Really good compilers do take into account the number of times an instruction cache would be cleared from branches, jumps, calls, and the level of optimizations selected during compile time can mitigate this problem. Some of the compliers are really really good at this, by actually counting instruction cycles and choosing the shortest paths. The bottom line is this. Assembly language can not exist without a jump instruction, but high-level/mid-level code can exist very happily without any goto statements.
@@iWhacko " Source code is meant to be readable". I have always stand on position that the source code is meant to create efficient and error-free binary. Readability is secondary. I have experience in nightmare projects that were killed by "good practices" and "code standards" and "clean code". Classic "measure becomes a goal" cases. Deadlines? Don't care, havta extract 5 more interfaces that will only ever have 1 implementation. Performance? Sorry, too busy renaming half of the functions in the project by adding "get" in front of the name. Bugs? Can't help, i am injecting 78th undebuggable maven dependency that obscures the logic behind reflection.
Meanwhile i was able to recreate 80% of the project from scratch on my own spare time, using 50 classes that compiled in 10s. Not 2000 AbstractIntgerStringPrinterFactory classes that compiled for 15 minutes because of 2 GB of maven dependencies. But it was all "good practicies" while my code was too "tightly coupled" so they went with continuing the garbage. Project went under because of poor performance, bugs and crossed deadlines. Imagine my shock.
for me, goto makes sense in pure imperativ code like Basic, but not for procedural and higher paradigmas like C, Java etc.
In pure C stuff like this is an at least decent to good way of handling things.
In most other languages you should rather use some sort of scope guards though (something RAII-like like objects in C++ and rust, or "using" resource statements in C# etc.)
And even if more modern low-level-like-c language like Zig where you don't have RAII (because no dtor) they added the statement defer and errdefer exactly to avoid this kind of code.
I think in C++ goto statements are also more complicated due to it being difficult to reason about RAII and object lifetimes in goto. At least I don't remember off the top of my head what the behavior is and I think a lot of programmers don't.
If, while, for, underneath they are just "gotos"
@@Proferk Hey, that's why C is just a macro assembler, right?
@@Proferk Any code we write that isn't 0s and 1s is just syntactic sugar for 0s and 1s!
Thank you for making this video.
Many people always scream at the sight of a goto without considering how it can be beneficial to the circumstance.
I personally write C# more than anything else so I benefit from 'using' statements, however I still find gotos can be very useful when writing state machines or non-LINQ filtering logic.
In everyday's code I barely use goto, but when doing code challenges like LeetCode or CodeWars using C# where I tend to stay away from Linq (it's slow + it trivializes some challenges), that's when "goto" statement feels the most useful. It's more efficient both space-wise and time-wise.
This has little to do with 'using' - in C goto may be helpful because it lacks control structures like try-finally and its APIs rely so heavily on return codes. Which is a nice way of saying that C lacks proper tools for certain things, and goto is just the lesser of a bunch of evils. But that does not make it good or safe. When you have a language that gives you better options, use the better options.
Goto breaks structured programming, and static analysis tools can't do their job properly without being able to make strong assumptions about the program flow. In C that may not be a big deal because you cannot make these kind of assumptions to begin with. But in modern C# static analysis is a big advantage. Tools rely heavily on knowing when something has been initialized and not, for example. In my experience many C folks cannot even appreciate that to its full extent because they have no experience with it. It would be foolish to compromise that just for something that might optimize the odd case by a few lines - and this is the reason why goto gets banned in so many projects.
Please note, it's not at all about whether goto is sometimes useful. It's about principles: If a feature is a bit useful, but sometimes dangerous, and there is another option - always use the other option. Don't argue with me, Douglas Crockford says that a lot, and he's probably smarter than most people here.
If you ever find yourself writing programs that look like that in Java or C#, step away from the computer. I don't argue about C, but as a senior lead developer and architect with 25+ years of experience, I say this with confidence: I have not seen one justifyable reason to use goto in C# application programming. Not even in 3D visualization or device driver code for industrial hardware.
Please consider making the course at one time payment. I think you would get more people to sign up. Thanks for making videos my main man
goto's are very handy in the case of gamedev - specifically when wanting to jump around within nested loops. Makes things 10x faster and organized if you do it right
Especially for text adventures
why is goto bad: it makes your code look more like assembly, and the whole point of programming languages is to not use assembly.
If you must use goto (usually to do something the language doesn't support, like breaking out of nested loops), use it sparingly and make it as clear as possible what it is doing, by using descriptive label names.
2:33: No, you print first so you are sure errno is not modified. Now, in this case there is no reason that errno would be modified, but that's not always the case.
This is basically the goto solution (pun intented) for handling errors in the Linux kernel.
That’s some very encoded humor.
I don't mind goto at all, but I think it should only be used in a very small scope. I think you can usually get a similar result through creating functions, so there is little need to actually have goto, but imagine you're searching for a specific value in a larger function and you have multiple nested loops for whatever reason. At that point you might actually want to have a goto in order to quickly break out of all loops. That is a perfectly valid use case in my eyes. The label is close enough to the goto for things to be understandable enough and you avoid having loads of breaks and if-statements. Could you just have made a function and returned the value when finding it? Sure. But sometimes you don't want to create a new function as it is not really a necessity in your use case. The goto will also be faster than doing if-statements every loop that break if true, which can matter a lot for larger data sets.
So as long as you keep things simple enough, I think using goto isn't bad. Just don't do a bunch of branching logic with them. Only use them as fast exits out of nested loops and such and you're all good, avoid them if necessary though.
Breaking nested loops is the pretty much the only reason I see to keep goto around. There are some other possible very niche uses, but generally this can be improved by refactoring code to be cleaner. In this video, he turned 13 lines of code into 17, and made a mess out of it.
The proper answer to this code chaos is (hold on tight): nest your code. Yes, you read that correctly: nest your code!
You started of quite nicely by introducing a return variable. This should ALWAYS be the very first declared variable in your code and set to THE error code, here -1. On success, the inner level of nesting, set it to 0. This saves a whole lot of lines assigning -1 to the error code variable.
Furthermore nesting provides a perfect way to achieve congruency between scope and lifetime of a variable. Actually it's the only way.
Once upon a time I worked like that.
We called 'epilogue' to labels at the bottom of each function to handle cleanup and having a single point of return.
When goto was considered a 'bad word', exceptions were invented.
Great video to remind that goto is not so bad, thank you
bool do_something_unguard(int *f) { }
bool do_something() {
bool b; int f = open();
if (f) b = do_something_unguard(f);
close(f);
return b;
}
// this gets boring fast, so does "goto exit:;"
// its almost like there's a reason for try/finally to exist...
I always found it an academic discussion. Because as you and I know in assembly a goto (jump) is required.
But in the extension of this discussion also was the “one return per function” something I break all the time these days.
Maintainability of code isn't an academic discussion. I'm sure everyone at your work place loves your spaghetti code lol
@@gregorymorse8423 you may not have watched this video then. The goto must go idiom is obviously not correct there are use cases where goto is a superior solution. Hence the clean code is not objectively true. And I can prove it even further, as jumps are Gotos and are the backbone of branching on a machine level. So the whole “clean code” religion breaks down the lower you go.
Just like the idiotic “one return per function idiom”. It makes code unnecessarily more complex and slower.
Then the “though shalt not repeat thyself” which is something you generally should stand by, but I have had situations where unrolling code is faster. And when there’s one situation where your “religion” doesn’t apply you can’t not should preach it as a fact.
And “spaghetti code” what is that?! Is it a jump? Because a call to a different function is also a jump and you need to grab the listing of that code and read it to understand it completely. Every program exists of different strands of “pasta”.
Ironically you can get far more list in Uncle Bob’s OOP world then a piece of assembly. Because it’s hard to find a single entry in complex frameworks like spring, spring boot, J2EE that have scheduled or event beams or even C++ which spawns many threads or systems with event messages.
So even “spaghetti code” isn’t an objective truth.
And it’s also a skill level! I’ve been developing software for 40 years. I find that the last 20 years I find it far easier to reverse engineer code than I did it the first 20 years. Just because I’ve seen more.
The first time I saw self modifying code, back in 1988 I was like: “wtf? I don’t understand what is happening here. Why is a memory location in code space updates?”
I spend literally a whole weekend experimenting with it and realized it’s power. And how I could reduce the amount of code needed and speed up for example joystick routines on the C64. Since I’ve seen self modifying code a lot and it’s just a matter of checking to see what opcode is set and it’s clear.
So yeah, even “spaghetti code” is busted as an objective scientific fact. Unlike ohms law that always is R=U/I or gravity is always 9.8M/sec and doesn’t deviate on Sunday’s or has an exception on Mount Fuji.
That is an objective scientific proof. It’s merely an academic discussion and personal preference that are not scientific facts. Because there’s an exception on everything Uncle Bob and his predecessors preached.
Cool, might actually make use of this in work next week. I had the issue that my C-code was too large to flash it on a microcontroller, and one thing I recognized was that I had such error-checks where each succeeding one only adds a line or two. Might give it a go (to).
yea people who unilaterally dismiss a basic thing like goto as bad just make it harder and more confusing for new programmers to figure out how stuff works and what they should be doing.
everything has a purpose, and most things have good and bad ways to use them.
For high-level language designers, thye must learn from examples where goto is essential in C and design features to avoid the need for goto, such as auto-dispose resources (RAII in C++ and Rust and using C#), or hand written state machines (async/yield).
The goto is literally not needed for any case whatsoever, in 20 years I couldn't fine any. Except the funny looking DuffyMachine, which is not even valid performance optimization anymore.
Well, jumping from a statement to another in a switch would be a legal use for "goto", but you don't need it, just put the switch inside a for-loop and change the expression of the switch and do "continue".
Hand written state machines can be created by mere function calls if you have a fix-point function to break the call-stack from becoming infinite. Its a common technique in Lisp. Maybe C programmers don't know that. Or maybe their compilers are too dumb to do RVO, aka, Return Value Optimization.
Something akin to a ".tail" for that in its IR. (almost all virtual machines that are JIT compilers can do that, its kind of a trivial optimization)
@@monad_tcp it's not needed, but it can make your code prettier and less repetitive in some rare cases (the linux kernel often uses gotos for error paths)
@@int32_ yeah, that's a well know deficiency of the C language.
that pattern is so used that compilers can actually do semantics analysis on it and I'm going to even admit that its the only valid use for goto.
besides jumping to another case in a switch which is a neat misuse of goto that does work and very little people know you can actually do that.
Impossible. Goto is essential for representing irreducible CFG (for all procedural constructs are, by definition, reducible). Which means, pretty much any mildly complicated FSM, including all kinds of parsing and tree walking. Languages without a goto are crippled languages.
@@monad_tcp lol, now try to implement an efficient indirect threaded code interpreter without a goto.
So in the "good" example for using "goto" I have this question: Why not simply make a method to which I pass the necessary parameters and then call close on them? What's the advantage of the goto in comparison? I might have missed it. But the duplicate code is not a problem to me as I can outsource any duplicate code into a method that gets the necessary parameters and just call that method instead.
Just now learning that goto and longjump are different. I always assumed the issue with goto was that it does what longjump does and lets you jump into a different function with the wrong stack setup
I would probably go with nested inversed if statements in a case like this, where instead of a cleanup on failure, you get a choice of continuation or local cleanup, which is then followed by the cleanup. Kind of like this:
errors = 0
do buffer thing
if (buffer succeeds) {
do socket thing
if (socket suceeds) {
do socket use
} else {
perror("sucket")
errors++
}
close(socket)
} else {
perror("buffer")
errors++
}
free(buffer)
return 0-errors
I remember in our first C# class the first task the teacher gave us was "make a loop".
I proceeded to write a goto variant for it with a break i made, the teacher was impressed but was like "okay, but please do the other way as this one is illegal" xd.
The reasoning explanation he had made was that unlike regular loops, this would make a randomized selection or something and that it would result in slower program in a sense.
I don't understand what your teacher meant to say, but you should absolutely not be using gotos in C#.
If you have to use a goto in C#, it is telling me one thing about your code - your function is too big and needs to be split into multiple functions.
@@lycanthoss Lol, I just said the teacher also said it was suboptimal, but he was surprised someone even knew about it, because people usually learning languages like python nowadays, etc. don't really learn this stuff at first.
It was back when i started learning and wanted to be a bit funny. Like yeah, it's not a good way, but a funny way nonetheless to make an infinite loop.
I'd never use it under normal circumstances. After asking around the only place where i could find use for gotos is maybe in kernel programming or so.
As some people already pointed out, they are just another tool in the box, and i am thankful for having them on C#, they are very rarely the way to go but it can make things a lot more clear and simple when used properly, but normal programmers rarely have the need.
For me i find it very useful for conditional retry logic and for exiting nested loops those cases are kinda rare.
You could argue that my code sucks then, but in that case i would like to learn a better way to do those that still work without overcomplicating simple things
What your teacher was probably talking about is ASLR, which randomizes addresses. This is implemented to prevent malicious attacks that tells the program to jump to an arbitrary line of code. If the attacker cannot be sure of the exact address of their desired code, then they cannot reliably execute arbitrary code or steal data.
The reasoning sounds like hocus pocus to me, why you _really_ shouldn't use it is because it makes everything harder to reason about. You could use a goto to implement ifs, fors, whiles, and functions, but just using the keyword designated for that purpose frees you brain up from having to follow the goto chain to figure out which one it is. Freeing up those mental clock cycles makes it easier to figure out whether or not your code goes into an infinite loop or any other bad state _before_ running it and having to use the debugger to figure out WTF is going on.
Another good use case is if you're in a nested for loop and one of your inner loops needs to break out of the entire thing, instead of having a bool that propogates breaks up the loops, you can just have a goto that leaves the entire structure.
I'm not sure, in c++, if this'll work with scoped variables, so that might be a case where you don't want to do it.
In this case, why would you not move all of those nested loops to their own function and simply return from it when you need to break all of the loops?
@@ChapmanWorldOnTube there might be a necessity to use a bunch pf local variables withion the structure, so you will need either to use global variables instead, that makes bug probability higher, or put all those stuff as a parameters into a function that is called from a single place of your code and depending on compiler that may cost you some perfomance and memory. Also the function will be stored at a different place in your code so you will actually need to spend extra attention to keep track of what is going on, so in this case a simple jump to a lable makes code actually cleaner and better in both readability and perfomance
@@WindLighter I of course don't share the same coding style or habits that you do, and I also don't code in C typically, so I hope you won't read it as confrontational that I disagree...
Of course any call to a procedure/function is a performance concern, however, IMO making function calls is far more unfairly demonized than go-to.
Function calling is a performance hit of a few milliseconds, which of course matters if called repeatedly in a loop - but presumably moving all of the nested loops within the isolated function means you need call it only once to initiate it - or at most, once per cycle of an outer-most loop.
In terms of readability - yes, when reading the calling code you might have to go locate the function elsewhere in the source code - a small chore should you need to. However, if the function is well named, in many cases you may be able to read past it without being concerned. In addition, I find locating and reading separate functions to be far easier than the alternative - one long sprawling list of nested loops gives me shudders. I envisage functions growing to many pages (and have worked on such code bases), and would very much prefer to have to go find a function, rule it out in terms of relevance, and return to the calling function to continue reading.
Parameter passing - If there are many local variables to pass then this could increase that function call overhead a little, but it also indicates a possible design flaw. Could the state information be wrapped up in a struct, which could be passed by reference? This isn't a cop-out, saying just pass a struct, but rather, all of those local variables must be logically relevant to each other (in abstract terms) to be required in the nested loops, and therefore could be conceptually combined into a struct representing the computational state, and with a suitable name to reflect what that is. From this the very concept of OOP falls forth.
Ultimately, goto is an individual choice or preference, but I feel if you're going to use it, it should always be a prompt to reconsider, is your abstraction of the problem a good abstraction.
@@ChapmanWorldOnTube The problem with that solution is that you then must unpack the variables from the struct. Whether they're passed on the stack, and thus take up valuable stack space, or passed by address while contained in a struct, it either requires extra space to be used up or extra syntax to be had. Both solutions are less easily understood at a glance, more difficult to type in, and potentially fraught with extra bugs, on top of requiring extra copying of values which in a long-running loop will add up. Now, if the compiler optimizes said functions because you label them static and use -O3, you still have harder to grok code with more potential for bugs.
@@anon_y_mousse We are clearly coming from very different places. I think I hold the opposite position on every point that you made. I don't code C anyways, I have never really liked the syntax. I have always been more in the Writhian camp. I will bow out on a mutual disagreement on this one.
goto statements are a powerful advanced tool. They should not be used by inexperienced developers simply because they're all too easy to abuse and make an habit of it, which is what used to happen long time ago - hence the stigma. But like anything else, goto statements should be encouraged whenever they are the best tool for the job.
Every expert-level assembly games programmer of the 70's-90's (and there was a lot) would agree.
Interesting. For the example you used as a good usage for a GOTO, I think the step-of-error-handling functionality *could* be reproduced using a switch statement instead (by passing in a value representing which step you are on, and letting that decide the starting point). But I think then use of a goto would make it more readable.
Still, I can't help but wonder if there's an even more elegant way of dealing with this kind of circumstance.
In this instance, while I love C and I still think it's the best language, C++ actually provides a more elegant solution whereby the cleanup code goes into the destructor for a class so that if any part fails you could simply return from the function and the appropriate cleanup would occur. Even though I borderline hate Zig for the syntax choices they've made, I'll admit that defer is also a rather nice option and slightly more flexible.
In more recent languages there is defer and sometimes errdefer which does essentially the same thing. In languages with lambdas and destructors you can synthesize defer by creating a type with a constructor that takes a lambda and a destructor that runs the lambda.
I remember in highschool the book had an entire page saying how goto is the black sheep of computer commands and mentioned that some guy wrote an entire book against the goto.
I wasn't taught back then how to use it and I wasn't taught how to use in the electronic engineering tei either(we only learned visual basic and c# there)
They are good for state machines in particular. Tail call functions are sort of better versions of them, and the difference is informative. Tail calls need to have context explicitly passed, while gotos retain the same context. This is why goto is so dangerous. When you read a block entered via goto you have no idea of what the context is (neither does the compiler), but responsibility for cleaning up the context is passed anyway!
We often see weird loop contortions in an attempt to make things that are not loops fit into them, and it really bugs me. State machines are some of the worst offenders, but maps are often maps in a programmer's head, converted to a loop for the code, then converted back to a map by the compiler for vectorisation! Goto is a fantastic pattern for building a state machine where each state is working on the same context, such as parsers. Sure, the control flow is unpredictable, and 'spaghetti like', but sometimes this is inherent to the system you are modelling rather than bad code.
Goto is a very crude tool, but most languages don't have better ones for non looping control flow. Tail calls are pretty close, but ideally you would want to avoid the function call overhead when you just want to build a state machine. I would like to see dedicated state machine control flow structures, more restricted than goto, but still able to handle non looping control flows elegantly without programmers having to translate it out of the form that it exists in their head.
The only go-to I ever used was in assembly. A manual step maker for a microcontroller that had to run regular checks for a critical value and therefore use a binary pointer with a bunch of gotos to do a bit of work and then return to the listen check.
I have a really good example. Imagine you have some loops, on inside another, and if a certain condition is reached, you want to break the first loop (or continue), in Java you have loop labels that allows you to use continue labelName; or break labelName; but in C++ we will have to use a goto instead. I think is a really useful use case
I usually just def a function, then call it in my other functions. This looks cleaner, so I don't need 7 functions for one action. Idk though.
Some asshole wrote a paper titled "goto considered harmful" and 56 years later idiots are misinterpreting it having only every heard the title and not the body and taking it as a universal law completely removed from it's original context. The paper was written in a time where FUNCTIONS were considered some confusing new-fangled invention by many programmers. The goal of the paper was to convince people that you should use higher level abstractions like functions. If you as a programmer in 2024 use functions (YOU DO) then you can COMPLETELY DISREGUARD "GOTO considered harmful" because IT'S NOT FOR YOU!
back in the day FORTAN was widely used - and it had 'computed goto' and 'arithmetic if' which could easily create messy code which is nowadays completey inconceivable
hold up. why at 1:54 does thy code viewer thingy appear to have negative-indexed lines
Quick way to check if the programming language you are about to learn is for babies or not: ask if the language supports goto.
Aside from getting out of nested loops, I will use goto for cleanup upon some error being detected when I allocated memory and there are multiple possible points of failure afterwards, as you illistrated in the video. I think this completely reasonable, and no amount of "goto is bad" will ever change my mind. This is essentially what "defer" and "errdefer" are doing in more modern languages.
I've only ever used goto in C where I would've used "break 2;" (or any larger number) in PHP. Honestly, I'm 50/50 on which is more readable: "break ;" is more readable in an immediate sense, but "goto breaknum;" is more readable when you want to figure out where things are going, especially if there's a complex nesting going on. Either way, it's better than the JS version, which makes you put the label _above_ the scope, then "break label;": i.e. "forlabel: for () { if () { break forlabel; } }" I get it, you're labeling the scope and then breaking from that scope, but it's still the worst of all worlds.
The point I made a half century OK, (OK, more than) is that you want to reading the static state of the code to give you an understanding of the dynamic state. Unrestrained gotos make this difficult. This is why we use words like "try/finally", "break", "continue" etc. to indicate that are doing constrained flows and thus our reading of the static code is consistent with the dynamic flow.
If you're using an ancient language and need to use GOTO, then annotate the usage to give the reader (and yourself) and even break (pun?) in understanding what is going on.
We are not allowed to use goto at work, combine that with the fact that we are using C++ 17 (much of it much older) … yeah, our code is completely unreadable. I loathe having to write messy code just because I'm not allowed to use goto. At least some code is Java, and where you have labelled breaks, so it is a little better…
in C++ in you can rely on destructors to do the clean up automatically. In C all of this is manual so you kinda need goto to make it cleaner.
Grew up typing in BASIC programs on my Commodore 64, and GOTO was very common. When I learned C in college, the course didn't even mention the keyword goto. It was years later that I learned that goto was recognized in C. I can say the ONLY time I ever used goto in C was when I was trying to debug something and used it to skip over a section of code.
A teacher told me that if I used "goto", he would make me fail all the exams I took where I used "goto" and that that sentence should never be used
There is simply no replacement for a computed goto. And languages that do not have it suffer badly in terms of performance.
BBC BASIC allowed computed GOTO/GOSUB, which I made good use of to replace long ON n GOSUB .... with GOSUB 10000+100*n
I also made use of computed RESTORE to save memory (Avoid reading ALL data into an array) with a simple RESTORE 10000+100*n
The C compiler will compile some switch statements into computed branch if the cases fit a pattern.
@@Lord-Sméagol and when you have a direct access to the computed goto, you can pre-cache the destination labels, while a switch will introduce a level of dereferencing with a label table. See how threaded bytecode interpreters use computed goto for example.
Another case I can think of is breaking out of a nested loop without introducing a one-shot flag variable, or likewise continuing the outer loop while inside the inner loop
I'm working with an SDK of a transaction processing system which uses callbacks to send events. The callback function is essentially a big switch/case statement processing the event the SDK sends. There's one case where I need to process a failed transaction which is executing a function and closing the transaction exactly as the successful case. I've been contemplating using a goto there for maintainability. There's a strong chance the successful transaction case will change in the future and might not get updated the same way in the failure case; the goto would make sure the code is only written once. However, using a goto in a switch/case is quite an anti-pattern.
the way i have experienced is that we tell it to newbies because newbies don't know how to write code and we don't want them to use it because they will be confused and write worse code. i have seen this live many times, while helping in uni.
I think there are some cases where you have an ultra fast loop or function whereby inserting a goto can save on t states. The jump instruction is really fast, plus also consider what variables are in the registers as this can make a big difference.
This is the first time i've ever I just instantly went and bought a course from a UA-camr as soon as you mentioned the C course. I fell in love with C after years and years of using higher level languages but learning materials were a bit sparse so I'm super excited for this even though I'm not a beginner in C, I'll learn a lot I'm sure! :)
Hey man, this is really cool.
I wanted to compare the code before and after the goto statements.
I wanted to check if a function call with a little refactoring wouldn't be an even better improvement, completely dropping the goto. A link to a gist or something would have been incredibly helpful for that.
Bad example to be honest. This case can easily be substituted by simply nesting the code.
Or, if you don't like deep nesting, by function calls. I.e.
int proceed_with_sfd(int fd, void *filebuf, int sfd) {
// Do stuff with fd, filebuf and sfd
return 0;
}
int proceed_with_filebuf(int fd, void *filebuf) {
int sfd = socket(...);
if (sfd == -1) return -1;
int retval = proceed_with_sfd(fd, filebuf, sfd);
close(sfd);
return retval;
}
int proceed_with_fd(int fd) {
void *filebuf = malloc(...);
if (filebuf == NULL) return -1;
int retval = proceed_with_filebuf(fd, filebuf);
free(filebuf);
return retval;
}
int main() {
int fd = open(...);
if (fd == -1) return -1;
int retval = proceed_with_fd(fd);
close(fd);
return retval;
}
But why risk the cache miss with a function call when GoTo achieves the same thing without the issue.
@@malcolmgruber8165 because this is 1000 times more readable and easier to debug. Following your logic lets write all the code in 1 giant function with goto jumps.
Not to mention that such small functions will be inlined by the compiler anyway. So that kind of premature optimization quickly becames pointless.
@@mk72v2oq Honestly, I find this a lot less readable. Now you have 4 functions for a trivial thing, and if you do this with all functions, you end up with a heck of a lot functions, all of which require a sensible and unique name. To follow the control flow I now have to scroll upwards and scan function names, which are going to get progressively worse the more you apply this, and keep track of what each argument actually is. It requires more code that you not only have to write, but also have to parse when reading it. And either way, you just have a single linear control flow with multiple entries to a single linear flow on failure. Except that in this case, both linear flows are scattered in the code.
@@jbird4478 well, it is the nature of C. In practice you usually can do it smarter and shorter though. The main goal here is to restrict area of responsibility of every function.
The problem with goto that it can be misused very easily. It is even more dangerous than regular unsafe C things.
But yeah, the language itself is inherently flawed in this regard. C is the last one where goto is even thinkable (if you don't still code in BASIC lol).
Embrace modern languages if you can. Functional elements make it way more clean.
Also, when control flow has conditional branches, it is not linear by definition.
@@mk72v2oq Any programming construct can be misused. It's the nature of C that you can use goto for such situations. And any paradigm can be over implemented. Limiting the responsibility of functions is generally a good idea, but it can certainly be overdone. To open a file, in practice one often has to do a few things more than just calling open(), but that doesn't necessarily mean they all have be separate functions. It can be practical to keep a reasonable amount of things in a single function.
And you're right of course. I should've put "linear" in quotation marks or something, because what I meant is that if all goes well there's really only one code path. And if you're reading it, that's generally what you want to know first. Then the points of failure are things you follow if needed, but in case that's also just a single path. So regardless of how you write it, what happens here is the code runs from A-Z, except it jumps out on failure. And that's precisely what the version with gotos reflect in the code. Higher languages provide other alternatives for this, like exceptions or scoped destructors, but C doesn't. C provides its own tools, and one of them is goto. Like all tools, there are situations to use it, and situations where you should not, but there's no reason to avoid it at all costs like some people seem to think.
I started programming in TI basic and got accustomed to using 'goto's just to realize that they are either not in or frowned upon in other languages. The irony is that asm is full of 'goto's in the form of 'jmp'.
I fully agree! This is great code. I do not write C too often, but I have been correcting some of my own code similarly to this. Albeit with a break statement in my case.
The point is to not write any more resource deallocation lines than allocation lines, as a good practice. In order not to accidentally leak resources or create duplicate frees.
As soon as you allocate stuff, you do not want to diverge. All code paths should come together at the end of your code.
It's good te remember that C is a low level language and does not have any try finally constructs or RAII features. And there's no GC. It's not a functional programming language where you can always do lots of return statements without any worries.
My teacher always got visibly upset when anyone used gotos 😂
What about using gotos to break out of multiple loops without annoying if statements and variables
Just make 3 cleanup functions that each call the next step below, if there is one, so if you fail early, you shortcut the stack, but if you fail late, you call the whole stack
The very few times when I used GOTO in my C code was always for the reason shown in this video, to avoid duplication of a clean-up code before returning from the function.
maybe it's a silly question, but why not add and call functions appropriately for subsequent cleaning steps? You'll still have clean code (and in my opinion even cleaner).... Maybe the issue is a bit slower performance due to pushing parameters on the stack etc., but I don't see any other disadvantages... and in nowadays we have so powerful processors that this shouldn't be a problem
So, since you mention switch (evil grin).
retval = 0;
if(error1)
{
retval = 1;
goto oops;
}
dostuff1();
if(error2)
{
retval = 2;
goto oops;
}
dostuff2();
...
if(errorn)
{
retval = n;
goto oops;
}
dostuffn();
do_more_stuff();
oops:
switch(retval)
{
case n:
tidyn();
case n-1:
tidyn_1()
...
case 1:
tidy1();
case 0:
return retval;
}
What could be clearer than (ab)using the fallthrough mechanic?
I remember even in late Visual Basic programming, there were sometimes esoteric cases where I actually resorted to those old GOTO/GOSUB statements. For example, VB doesn't have a "continue" feature inside loops, so if I needed to do that I'd label the end of the loop (e.g. "ContinueHere:") and GOTO that point.
It also specifically had the language feature "On Error GoTo ..." for the use case similar to the one shown in the video.
@@Hublium Yup, I remember using that. Especially when dealing with file I/O. A bit more dangerous was the "On Error Resume Next" which was super convenient if you needed to test just one call that might error out, but you had to remember to turn it off again (On Error Goto 0) or you've just suppressed all runtime errors globally until program execution terminates, potentially opening up all kinds of glitchy misbehavior downstream. Have fun debugging THAT!
An exception in C++ is basically a goto chain. Everywhere, where throwing an exception in a higher language would be appropriate I consider a valid use of goto. Something resembling try-catch-finally would be an unreadable mess in C without goto
I did exactly this for my college project, because using early returns seemed more messy. I realized that 'goto' is not some forbidden practice and this is a valid use-case. Like with breaking out from an inner loop.
Is this a normal practice to do with C in a professional environment?
It depends. But in embedded C, it's standard practice to use goto (as far as Ive seen)
I’ve never seen goto/raw jumps as illegal in whatever language I was using that had them, I just considered the results of their usage to be more _potentially_ uncontrollable or unpredictable and therefore requiring more discipline, foresight/planning, and intention in code than “standard” conditional logic conventions.
Another use of goto is to escape nested loops nicely.
Also if you're using C++, the compiler cleans up if you use goto to leave an inner scope just like with other control flow statements, but it may leave variables in an ill-formed state if you go past their declaration.
One thing I still find hilarious is that Java has goto as a reserved keyword just so nobody can use it. 😆
I heard that goto keyword is used internally in javac
but it's true that the labelled-block-break mechanism of java can almost always do the same thing, and sometimes it's clearer to have the 'section that can be jumped out of' in a block like that.
I made a DFS klotski solver, and using a normal recursive function it gave me segmentation fault, but doing the recursive parts with goto worked flawlessly
I remember our high school computer programming teacher in the mid to late 90s didn't even want us talking about the goto statement. It was a bit of a joke. Growing up in the 80s, home computers like the Color Computer just didn't have the fancy loops and structures of while or case. Except for for/next, we only had goto for jumps.
Goto is a must to make code readable, although it hurts readability when used improperly.
It’s nice to have “finally” in higher level languages 😅
In C# your example would be:
using (open file)
{
do something
}
that's all. If "do something" fails it will close object that was created in using section - in this example close a file.
Also similar think you can make with try catch section.
You can use functions with return.
Also using break and continue.
The only reason to use goto is when you want break from double loop. (Loop in a look). Break will exit from one loop only.
goto is used extensively in the Linux kernel. If it’s good enough for Linus, it’s good enough for me.
Is this example completely solved by Go's defer statement?
I usually only use goto in command line apps where I need to validate inputs. Invalid input? Move back to the line that takes the input. Super simple and readable
When I use goto, it’s only used for the resource cleanup and error code return when something wrong happens in the middle of the function. It really helps me keep away from nested and duplicated code. I can’t think of any other good usage of the goto keyword.
I use goto in switch statements instead of breaks, because it's faster? I don't think that's bad practice right.
The biggest problem to my knowledge with goto is the potential to mess up the stack. It’s generally just safer to build off the stack with a series of function calls than create a state where the call stack is uncertain
Gotos are fine when used properly. I only use them inside of functions to jump to a cleanup or error case.
The main use I have for goto is breaking out of nested loops. Yes, it's possible to do it without goto, but most methods for that make the code less readable to me. People only really do that for the sake of not using goto.
We have it so good and rough these days. IF and GOTO were the only flow controls we had. There was no malloc or garbage collection. We had to remember the addresses of the ROM and RAM usable memory registers and read/write them directly with PEEK and POKE. You could make a sheet of graph paper into a chart and memory map the entire program and map the execution flow end to end before typing anything if you wanted.
What's old is new I guess. We started with strict typed languages and mainframes, then we wanted dynamically typed user-friendly languages and our own servers in the enterprise, now we are back on the strict typed language kick and cloud computing as a modern replacement for the mainframe.
Just pointing out a little error in the first version of the error handling code. You should call perror first before calling any function that could set errno. In this case the error message won't be very helpful since it will print the termination message of the close function instead of the malloc error.
Great video 😁
Not like it really matters, the point of the examples was to demonstrate a good usage of goto, not to show perfect code
really cool feature of C. thanks for sharing.
retval could have been set to -1 at startup instead of before every goto statement 🤓
Pre-emptive guess: Because humans make mistakes, and Gotos divorce syntax from the underlying logic, making mistakes more likely (e.g. accidentally reusing a Goto label, using the wrong label, reusing a variable from another part of the code)
That last case you mentioned (the good use of a goto) feels like a switch statement in a way.
I supposed as most languages I use don't have goto, I'd increment or set a numeric variable and then switch() it for error handling.
Great video! I learned a lot :)
Interestingly with a switch in a loop or loop in a switch, break and continue operate differently.
@@gregorymorse8423 Huh, didn't know that.
Cheers!
@@AterNyctos yes break in switch always breaks the switch. But continue in a switch effects the loop. Weird aspect of C syntax
@@JodyBruchon you obviously are extremely ignorant. Switch statements are jump tables except for ones with only 2 or maybe 3 branches. You literally have no idea of how modern processors and compilers work. You've given serious evidence.
@JodyBruchon maybe I'm being a bit harsh but seriously Wikipedia "branch table". Practically speaking this knowledge is important though with a good compiler perhaps it needn't be.
Goto has a place, here's my easy advice, if I don't have to use them, if there is a more logical readable way, I'll use it, The compiler can handle making the goto statements, which just become a jmp in assembly anyway. A great example of the compiler using a jump instruction to great effect, take C#, make a switch case statement. If it has more than a few elements, and they are sequential (if your cases aren't make your cases into an enum, so it's sequential) it makes a jump table. Which is.... goto values under the hood, However, this means that the resulting code is easier to follow the execution flow. In oop, goto is generally frowned upon. Even though under the hood the IL and the assembly is using it to great effect. If you're writing procedural code? or following functional programming workflows? They can be incredibly useful. Are you getting low level, programming for bash or batch files? The scripting you'll need to use goto: labels. Basically. It comes down to situation. Most modern languages we use them extensively.. we've just abstracted them away into inbuild functions that handle all the madness of jumps and pointers for us. Lower level, we've abstracted them to "Here take this pointer, do the thing at that location" aka... methods, subroutines, functions, whatever you want to call them. Then lowest level if this then goto:
It's not that they are evil, or illegal, it's just most languages we've evolved the need for them out, the compiler handles all of that for you, so if it's not a weird edge case, you shouldn't need them. However edge cases and older languages exist, and sometimes, in the name of performance, those layers of abstraction are an obstacle and you just have to get low level and handle that stuff.
Could've initialized retval to -1 to save a line per error case. It also would've implied that you don't succeed until you do.
Also, if you've ever read a reasonably complex Batch script in Windows/DOS, you'd know that gotos were the closest thing to functions unless you enabled extensions. Cascading goto error handling was also commonplace.
Finally someone illustrates how gotos can be acceptable in some situations. People gets blind with these things...
Use of "goto" being good or bad also depends somewhat on the language.
For instance, this error handling case is demonstrated in C, but in a language which supports try..finally (such as C# or modern Pascal variants), it would be at least more readable, if not more elegant, to dispose of invalid resources in a finally block, having already logged the error state. This is even more true in the (often very misunderstood) modern Pascal case, because pascal allows you to set the return value of a function without returning from it.
The real problem with "goto" is that it's akin to a break / return, but without a boundary (the outer loop, function, method). This makes "goto" very easy to abuse in terms of code readability. Readability is of course somewhat subjective also, and so my argument generally falls to this: I've been programming various languages for almost 30 years commercially, and longer as a hobby, and I've never yet found myself in a situation in which "goto" is absolutely necessary - there's always another way to solve the problem. That being the case, if there's potential for it to upset the readability of control flow - it's probably best to simply avoid it.
The counter argument, I understand, is that ultimately all code boils down jumps (essentially "goto") at the assembler / machine code level. I feel this argument misses the point of being a good citizen with regards to readability at the higher level. I don't think anyone could argue that "goto" can't be used to create rats-nest code, so if you could convince me that there's a case in which "goto" is the only solution, I might well reverse my position on it - but for now I remain firmly in the "goto" == bad camp.
Even simply having exceptions opens another can of worms, especially the exceptions + destructors combination. They solve one problem, while introducing another one, and now your program has to maintain a lot of runtime information internally, just for a tiny bit of convenience for programmer. Just having gotos isn't as ugly in comparison.
In strict terms, no one path is necessary as you can rework the code to your heart's content and make it amazingly convoluted and difficult to debug just by being an ideological drone that spouts nonsense like "goto is evil". That doesn't mean it's the best decision, and possibly not even a correct one, but you can do that if you like to leave the world a worse place than you entered it just for ideology. Or, and I'm just throwing this out there, you can use goto in places where it not only streamlines the code but improves the clarity of it and try to teach those that follow you the correct usage of the construct instead of telling them to use a worse solution.
@@anon_y_mousse Calling it an ideology is a strawman argument. This debate has been going on since the 60's, perhaps even earlier. I gave reasoning behind my position, that is not ideology.
This would be akin to me telling you that you are only in favor of goto because you have an inferiority complex - I am sure this argument would be unfair, you likely have quite different experiences that lead to your position.
@@ChapmanWorldOnTube No, it's an ideology because all the arguments against using goto are invalid. Essentially it all boils down to saying you don't like the way a certain method of flow control looks and thus you hate on it and generally anyone who uses it. If you use any imperative programming language then hating goto is nonsensical. The only way to escape having the look and feel of goto is to use a declarative language, and even then it's still only thinly veiled due to the way a computer operates.
@@anon_y_mousse And this is another straw man.
To paraphrase you "All arguments against goto are invalid because it boils down to you saying you don't like the flow control implications..."
This is not true. The arguments against goto aren't merely opinion. Edgar Dijkstra's "Go To Statement Considered Harmful" letter didn't say "I don't like it therefore it's bad" - Instead it carefully explained that there was a measurable drop in quality in software that was proportional to the number of times that goto is used.
It then goes on to reason about why this is, with the hypothetical conclusion that programmers, being human, had increasingly diminishing capability to retain the context of control flow the more goto is used. This reasoning was followed up on in much research since, and found to be valid.
Goto being bad is not about a coder looking at the text and thinking it looks ugly, nor is it about following some dogmatic idea that some authority says its bad so it must be.
Instead, goto being bad is based on two issues. First that it allows the programmer to violate the closure of a function/method as a feature, and second that it makes code objectively (not subjectively) more complex and thus more difficult to understand.
Side note: When someone says that code is less "readable", what this *should* mean is less "understandable". Many make the mistake of thinking that readability is an aesthetic thing, like the decision between putting spaces inside of parenthesis or not, or to use tabs vs spaces. Such things are a style preference and not really relevant today. In modern times we've all read other peoples code online for one reason or another, and if we don't like the styling we can simply change it. Instead, what makes code "readable" is how easy it is to understand.
Developers often pride themselves on how clever they are, but rarely consider if their code is understandable to others.
A rats-nest of goto is, again objectively rather than subjectively, less easy to understand.