Live data from Hacker News

GOTOphobia considered harmful in C

blog.joren.ga

51–60 of 319 posts

Re: GOTOphobia considered harmful in C

#51

The C version: int* foo(int bar) { int* return_value = NULL; if (!do_something(bar)) goto error_1; if (!init_stuff(bar)) goto error_2; if (!prepare_stuff(bar)) goto error_3; return_value = do_the_thing(bar); error_3: cleanup_3(); error_2: cleanup_2(); error_1: cleanup_1(); return return_value; } The D version: int* foo(int bar) { scope(exit) cleanup1(); if (!do_something(bar)) return null; scope(exit) cleanup2(); if…

It's still wrong by default.

The correct pattern here is RAII, which requires no explicit cleanup code so there's no forgetting to use it.

Re: GOTOphobia considered harmful in C

#52
I actually prefer "goto-less alternative 2". It's more verbose, but more explicit. No magic, I know exactly what happen. If you suddenly have more functions, you should have an array of functions with a clean_up_level variable.

Of course, you probably should not have a global state that some clean up function with a side effect deals with in the first place, but it's the c linux kernel so I assume there is something I don't know.

Re: GOTOphobia considered harmful in C

#53
post #9

There have been a bunch of major security vulnerabilities due to mistakes involving GOTO in C, being used as suggested by the article. Here’s a memorable one: https://www.imperialviolet.org/2014/02/22/applebug.html

That error is not due to goto, it was just a goto which was errously executed because of badly formatted code. (It looked like the statement was inside an if-block due to the indent.) Pyhon would have prevented this bug, but so would a formatter. Rust also requires braces for if-blocks to prevent this kind of error.

"The problem isn't C, it's that they weren't using C properly..."

Re: GOTOphobia considered harmful in C

#54
post #22
post #10

> Bad code is the product of bad programmers This person is living in a pretend bubble that isn't grounded in the reality of large projects, multiple team members, deadlines, changing requirements, etc. No programmer is perfect. And when your tool can cut your arm off, you should be careful or route around the dangerous bits when possible.

This analogy implies that all other tools are 1000% safer, but they are not. In C it’s like pointing to a dusty corner behind a table in a room full of dirt. When was the last time you did “cut your arm” with goto specifically? What’s the count and time ratio to other issues? Were these also addressed as taboo or left as “experience earned”? Gotophobia in its largest part is just a stupid meme with no real world data…

Where do I imply other tools are 1000% safer?

Even 30% safer is a win.

30% safer, 30% more readable, and 30% more productive would be even better.

> When was the last time you did “cut your arm” with goto specifically?

It's been a while since I've used C, and even longer since I've personally written goto statements. I do remember frequently getting tripped up on them right after undergrad. It's not friendly, and I don't ever wish to touch them again.

I'm working in a C++ game engine project right now and it's constantly segfaulting. I can't imagine that setting register jumps manually in complex higher level code would improve my situation.

When I get to choose the language, I use Rust. It fits the C use case and fixes many of the warts.

Re: GOTOphobia considered harmful in C

#57
post #26

> On the other hand, today we have the very opposite situation: programmers not using goto when it's appropriate and abusing other constructs, what ironically makes code only less readable. One I see all the time from beginners is creating a finite state machine using one method per state and jumping between states by calling the next state’s method from within the current state’s method. Essentially just emulating g…

This is one of those problems that Tail Call Elimination cleanly addresses. The "obvious" approach to writing an FSM does exactly what you'd expect.

Re: GOTOphobia considered harmful in C

#58
post #10

> Bad code is the product of bad programmers This person is living in a pretend bubble that isn't grounded in the reality of large projects, multiple team members, deadlines, changing requirements, etc. No programmer is perfect. And when your tool can cut your arm off, you should be careful or route around the dangerous bits when possible.

I see this "if you're good then you don't need safety" mentality in a lot of conversations with C programmers about programming languages.

Maybe it's some kind of defencive response against newer programing languages slowly eating up spaces C used to be dominant in (like command line tools and system daemons), maybe it's just the programmer saying this thinking they're that special. Either way, most people saying this are setting themselves up for failure.

I wouldn't start a new project in C unless I absolutely have to but if you disagree, you can at least admit that there are dangers in C that you need help with if you want to be sure you're doing everything right. With most warnings treated as errors, extended warnings enabled, linting to make things like missing brackets obvious, static analysis of all code to spot difficult bugs, automated dynamic analysis of test cases and a proper testing pipeline I believe you can write C code that's safe enough.

In this case, I'm willing to give the authors the benefit of the doubt because goto hatred is worse than the risk posed by goto in most settings. Gotos used right are fancy if/switch statements and avoiding them in C can lead to a mess that doesn't add much safety. Most examples given are better in my opinion, because using goto can replicate the code flow modern languages provide with things like when/match/defer keywords.

Re: GOTOphobia considered harmful in C

#59

The C version: int* foo(int bar) { int* return_value = NULL; if (!do_something(bar)) goto error_1; if (!init_stuff(bar)) goto error_2; if (!prepare_stuff(bar)) goto error_3; return_value = do_the_thing(bar); error_3: cleanup_3(); error_2: cleanup_2(); error_1: cleanup_1(); return return_value; } The D version: int* foo(int bar) { scope(exit) cleanup1(); if (!do_something(bar)) return null; scope(exit) cleanup2(); if…

Plain C version with attribute((__cleanup)):

    int* foo(int bar) {
        int* return_value = NULL;

        __cleanup__((cleanup_1)) int c1 = 0;
        if (!do_something(bar))
            return NULL;
        __cleanup__((cleanup_2)) int c2 = 0;
        if (!init_stuff(bar))
            return NULL;
        __cleanup__((cleanup_3)) int c3 = 0;
        if (!prepare_stuff(bar))
            return NULL;
        return_value = do_the_thing(bar);

        return return_value;
    }

Re: GOTOphobia considered harmful in C

#60
Alternative 6: keep a list of closures to execute before returning. Then regardless of the length or complexity, it's always just `cleanup(); return`.

For a structured version of ^ that, see Go's defer.

Post reply on HN