Search code examples
ccoding-style

Why are goto, break, continue and multiple return statements considered bad practice


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?


Solution

  • 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