Since Dijkstra's famous article in 1968 the phrase goto considered harmful is often used to denounce each and every goto statement. However, sometimes goto is just fine.

Cleanup

The most usual use case for goto is cleaning up things. Depending on how far the setup procedure comes, the tear down procedure must do the right cleaning. With goto, it looks like this:

x = allocate();
if (!conditionA(x)) goto errorA;
y = allocate();
if (!conditionB(x,y)) goto errorB;
z = allocate();
do_something(x,y,z);
deallocate(z);
errorB:
deallocate(y);
errorA:
deallocate(x);

If you want to remove the gotos, then you usually use nesting like this:

x = allocate();
if (condition2(x)) {
    y = allocate();
    if (condition3(x,y)) {
        z = allocate();
        do_something(x,y,z);
        deallocate(z);
    }
    deallocate(y);
}
deallocate(x);

There are other alternatives, but they are worse. For example, you could duplicate the cleanup code, which makes it very error prone, because you will forget some cleanup.

More modern languages like D and Go, provide other means for cleaning. While this is not available in C, this resolves to an elegant version without gotos:

x = allocate();
scope (exit) deallocate(x);
if (!conditionA(x)) return;
y = allocate();
scope (exit) deallocate(y);
if (!conditionB(x,y)) return;
z = allocate();
scope (exit) deallocate(z);
do_something(x,y,z);

Finite State Machines

Coding an FSM is more rare than the cleanup procedures, but in this cases goto really shines.

start:
switch (getInput()) {
    case a: goto stateA;
    case b: goto stateB;
    default: goto failure;
}
stateA:
switch (getInput()) {
    case a: goto failure;
    case b: goto stateB;
    default: goto end;
}
stateB:
switch (getInput()) {
    case a: goto stateA;
    case b: goto failure;
    default: goto end;
}
end:
return SUCCESS;
failure:
return FAILURE;

That could be written with switch, but the code is harder to read and maybe slower.

state = START;
while (state != END) {
    switch (state) {
        case START:
            switch (getInput()) {
                case a: state = A; break;
                case b: state = B; break;
                default: state = FAIL;
            }
        case A:
            switch (getInput()) {
                case a: state = FAIL; break;
                case b: state = B; break;
                default: state = END;
            }
        case B:
            switch (getInput()) {
                case a: state = A; break;
                case b: state = FAIL; break;
                default: state = END;
            }
        case END:
            return SUCCESS;
        case FAIL:
            return FAILURE;
    }
}

Eventually, goto is just fine, if you know when to use it.

© 2013-05-14
qznc