0

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?

user2248702
  • 2,741
  • 7
  • 41
  • 69
  • 3
    Never heard about this strange "requirement". Best thing you can do is to ask your instructor or lecturer. – iBug Jun 11 '22 at 04:13
  • Did you try looking for answers on [softwareengineering.se]? – Dev-iL Jun 11 '22 at 04: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. – jamesdlin Jun 11 '22 at 04: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. – chux - Reinstate Monica Jun 11 '22 at 04:23
  • Don't they have opinions about _"mountains of nested `if` statements"_ too? That's usually avoidable by making short functions which does "one thing" only. – Ted Lyngmo Jun 11 '22 at 04:29
  • 1
    @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. – Gabriel Staples Jun 11 '22 at 05:05
  • @jamesdlin, exactly! I stated that too, in my answer: `goto` is best used to create functions with a single `return` statement to exit. So, no `goto` _and_ a single `return` is a bit of a contradictory requirement. – Gabriel Staples Jun 11 '22 at 05:05
  • Matter of opinion. – n. m. could be an AI Jun 11 '22 at 05:05
  • @GabrielStaples The good thing with short functions that does one thing _is_ readability and to limit the amount of nested `if`s. It simplifies testing too. I don't consider reuse a requirement for putting sections of code in functions. – Ted Lyngmo Jun 11 '22 at 05:22
  • @TedLyngmo, yeah, I have seen code both ways. It's a matter of taste, for sure. I _hate_ `auto` in C++, for instance, because it makes the code so _unreadable_. Others _love_ `auto`, because it makes the code so _readable_. Wait, that's opposite! How can it be both? Different brains I guess. – Gabriel Staples Jun 11 '22 at 05:25
  • @GabrielStaples :-) I love `auto` for iterators and generally when writing generic code. I don't love it as much when reading complex code where it's not obvious what type `auto` really is. :-) – Ted Lyngmo Jun 11 '22 at 05:29
  • @TedLyngmo, I've had tons of issues there too! The type of the iterator in an `unordered_map`, for instance, is a `std::pair`, giving it the `.first` and `.second` member variables. Not obvious to anyone new to `unordered_map`s. So, I personally updated the examples in the cppreference reference wiki for it here to show that: https://en.cppreference.com/w/cpp/container/unordered_map. – Gabriel Staples Jun 11 '22 at 05:32

1 Answers1

2

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
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • _"especially if you jump outside your function"_ - How is that even possible with `goto`? – Ted Lyngmo Jun 11 '22 at 05:32
  • @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. – Gabriel Staples Jun 11 '22 at 05:35
  • @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](https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world) repo. – Gabriel Staples Jun 11 '22 at 05:36