Statement goto can not cross variable definition?

coanor picture coanor · Jan 11, 2013 · Viewed 25.3k times · Source

Suppose these code compiled in g++:

#include <stdlib.h>

int main() {
    int a =0;

    goto exit;

    int *b = NULL;

exit:
    return 0;
}

g++ will throw errors:

goto_test.c:10:1: error: jump to label ‘exit’ [-fpermissive]
goto_test.c:6:10: error:   from here [-fpermissive]
goto_test.c:8:10: error:   crosses initialization of ‘int* b’

It seems like that the goto can not cross pointer definition, but gcc compiles them ok, nothing complained.

After fixed the error, we must declare all the pointers before any of the goto statement, that is to say you must declare these pointers even though you do not need them at the present (and violation with some principles).

What the origin design consideration that g++ forbidden the useful tail-goto statement?


Update:

goto can cross variable (any type of variable, not limited to pointer) declaration, but except those that got a initialize value. If we remove the NULL assignment above, g++ keep silent now. So if you want to declare variables that between goto-cross-area, do not initialize them (and still violate some principles).

Answer

Jan Hudec picture Jan Hudec · Jan 11, 2013

Goto can't skip over initializations of variables, because the respective objects would not exist after the jump, since lifetime of object with non-trivial initialization starts when that initialization is executed:

C++11 §3.8/1:

[…] The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and

  • if the object has non-trivial initialization, its initialization is complete.

C++11 §6.7/3:

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5).

Since the error mentions [-fpermissive], you can turn it to warning by specifying that compiler flag. This indicates two things. That it used to be allowed (the variable would exist, but be uninitialized after the jump) and that gcc developers believe the specification forbids it.

The compiler only checks whether the variable should be initialized, not whether it's used, otherwise the results would be rather inconsistent. But if you don't need the variable anymore, you can end it's lifetime yourself, making the "tail-goto" viable:

int main() {
    int a =0;
    goto exit;
    {
        int *b = NULL;
    }
exit:
    return 0;
}

is perfectly valid.

On a side-note, the file has extension .c, which suggests it is C and not C++. If you compile it with gcc instead of g++, the original version should compile, because C does not have that restriction (it only has the restriction for variable-length arrays—which don't exist in C++ at all).