1

I've recently been taking a C course as part of a university degree. We have been forbidden from using goto, break and continue, and must only use a single return statement from a function.

I have been writing C for many years as a hobby without these restrictions and have been struggling to write in the style they require - often ending up with mountains of nested if statements. I try to avoid goto where possible, but have a habit of using break, continue and multiple return statements very liberally and have never had any issues.

Why are these features considered bad practice?

12
  • 4
    Never heard about this strange "requirement". Best thing you can do is to ask your instructor or lecturer. Commented Jun 11, 2022 at 4:13
  • Did you try looking for answers on Software Engineering? Commented Jun 11, 2022 at 4:14
  • 4
    That seems like a contradictory set of requirements. Following a single-entry, single-exit (SESE) pattern typically explicitly recommends using goto. In C, single-exit often can be a good idea since C lacks RAII, and having multiple exit points makes it more likely that a function will fail to properly free resources on all of them. goto, when used properly, is great for managing resources without an indentation pyramid of doom. Commented Jun 11, 2022 at 4:16
  • 3
    user2248702, "Why are these features considered bad practice?" --> not always bad practice. Code for clarity. Use break, continue, goto when needed. avoid them, but use them when it is the best choice - which is usually rarely. Commented Jun 11, 2022 at 4:23
  • 2
    @TedLyngmo, and then what about mountains of tiny functions which are never re-used when you could have the logic cleanly in 1 single 100-line function! I break out code into multiple functions for 1) reuse, and 2) readability. A single well-written 200 line function is more-readable than a dozen functions all calling each other once, without real re-use nor need to be multiple functions. It's all tradeoffs. Commented Jun 11, 2022 at 5:05

2 Answers 2

4

This is highly subjective, as all 4 of those things have trade-offs--pros and cons.

Ask your teacher, and report back here.

goto has an unnecessarily bad reputation. People fear it waaay more than they should! I think it is because using goto can be easily abused, especially if you jump outside your function, or jump backwards (upwards in the code). If you jump only downwards, however, and within the same function, not crossing over variable declarations in the process, goto can be an excellent technique to help you achieve a clean single return statement at the end of a function. In C, it can be the perfect way to handle errors in complex initializations! See my usage in my answer here: Opaque C structs: various ways to declare them. See also the Additional reading and justification for valid usage of goto in error handling for professional code section at the bottom of my answer there.

In some organizations, including some safety-critical embedded microcontroller applications I have worked in in the self-driving car industry, using goto can even be required. I've worked in code bases where the above error handling technique with a single return and using goto was required by my organization's coding guidelines. It's not automatically evil. Rather, it's a tradeoff with pros and cons. Unfortunately, many people I've worked with also have an unfounded revulsion towards goto which was probably planted by their teacher, like your teacher for instance. This revulsion can lead to some pretty awful code at times when goto would have beautified and simplified it tremendously.

Note also that in C++ there are other ways to do error handling that aren't as accessible in C, but still, goto could be used.

break and continue are much less-easily abused. Perhaps the only real argument against them is code clarity.

Ex: do something 10 times:

// fine, but perhaps not as clear const int NUM_TIMES = 10; int i = 0; while (true) { printf("i = %i\n", i); i++; if (i >= NUM_TIMES) { break; } } // clearer (withOUT `break`) int i = 0; while (i < NUM_TIMES) { printf("i = %i\n", i); i++ } // or for (int i = 0; i < NUM_TIMES; i++) { printf("i = %i\n", i); } // The above example is trivial, but imagine you are NOT just // incrementing a number! The non-`break` while loop with your // condition up-front can be clearer. The `break` option is just // fine too, but it's a matter of taste. 

A single return statement is best achieved when using goto. Otherwise, it requires tons of nesting. So, that is contrary to your teacher's guidance, it seems.

Many people, however, opt for multiple return statements to exit a function early and avoid tons of nested braces. It's basically a tradeoff of:

  1. tons of nesting, OR
  2. goto, and a single return, OR
  3. multiple returns

In languages like C, I use goto and a single return, unless my stinking peers won't approve it, and they fight with me, in which case I conform to whoever is reviewing me so I can get my stinking code merged. :) (Replace "stinking" with "beloved", for your peers, of course).

In languages like C++, where developers hate goto even more than in C, I generally use multiple returns or extra nesting and breaking the code out into sub-functions--again, to appease my peers who don't know and love C.

In Python, where goto does not exist, I use multiple returns.

At the end of the day, ask your teacher. Take mental notes. Learn why they are doing it. Learn from us. Learn from books. Form your own opinions. Begin recognizing trends and "best practices", and form your own opinions. If you need to do what your teacher wants, even if they're wrong, for the grade, do it. But do it the "better" way you determine outside that class.

In code reviews, if your stinking peer won't approve your beautiful goto usage, try to convince them. If you can't, make the change to avoid goto, reduce friction, and move on. It's not worth fighting about. Everything is a tradeoff. There are many "right" ways to do things.

Note to all: I say "stinking" in jest. I am very grateful for the job that I have.

TODO (Notes to self):

Try forcing goto to jump outside the function, even if it produces undefined behavior, as a demo to prove it is dumb:

  1. goto jump outside function
  2. https://stackoverflow.com/a/44843514/4561887
Sign up to request clarification or add additional context in comments.

3 Comments

"especially if you jump outside your function" - How is that even possible with goto?
@TedLyngmo, I haven't actually tried it, but I'm pretty sure if you disable enough warnings and errors in your compiler flags, you can make goto jump anywhere in the code. Maybe was more common in the very early days of programming with older compilers? Compilers don't have to produce runnable or correct executables. They just have to produce executables.
@TedLyngmo, I may come back to this and try that sometime, but it's not high priority right now. If I do, I'll add it to my eRCaGuy_hello_world repo.
0

With (purely) structured programming you can look at the structured statements to get a feel for the flow of the logic in your function. The flow is clearly visible since the contained statements are indented. Also, when you see a statement like

while (someCondition) { ... } /*Is someCondition false here?*/ 

you know that the contained statement sequence will be executed zero or more times until someCondition is false. However, with goto and goto-like statements (break, continue and early return) you throw this out of the window. You now have too look really carefully at every line for jumps buried in the code. Statements like the one above can potentially be lying; if there is a break in the middle of the while statement, someCondition may be true after the while statement has been executed. Ouch! With early returns you also cannot have post-conditions at the end of a functions, i.e. conditions that must hold true for the result or before the function returns. So to summarize: The virtue of structured programming is that it makes programs easier to reason about.

5 Comments

The virtue of structured programming is that it makes programs easier to reason about. Except when it doesn't. Forcing an algorithm into some predefined straitjacket implementation makes programs harder to reason about.
@AndrewHenle Only when this "forcing" is done in a substandard way.
Force-fitting an algorithm into some unnatural flow just to fit some preconceived, close-minded way to write code is substandard.
@AndrewHenle Please point me to such an algorithm that does not lend itself to pure structured programming.
How about you prove such an algorithm doesn't exist, since you're providing this as an answer?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.