Ist das Springen über eine variable Initialisierung schlecht geformt oder verursacht es undefiniertes Verhalten?

17

Betrachten Sie diesen Code:

void foo()
{
    goto bar;
    int x = 0;
    bar: ;
}

GCC und Clang lehnen dies ab , da der Sprung zur bar:Umgehung der Variableninitialisierung erfolgt. MSVC beschwert sich überhaupt nicht (außer mitx After verwendet bar:wird, wird eine Warnung ).

Wir können etwas Ähnliches tun mit switch:

void foo()
{
    switch (0)
    {
        int x = 0;
        case 0: ;
    }
}

Nun alle drei Compiler Fehler aus .

Sind diese Schnipsel schlecht geformt? Oder verursachen sie UB?

Früher dachte ich, dass beide schlecht geformt waren, aber ich kann die offenbarenden Teile des Standards nicht finden. [stmt.goto] sagt nichts darüber und [stmt.select] auch nicht .

HolyBlackCat
quelle
1
Problem wäre trivialer, wenn Sie xnach dem Sprung verwenden.
Jarod42
1
nicht der Standard, aber hier finden sich einige Informationen dazu: en.cppreference.com/w/cpp/language/goto im Besonderen: "Wenn die Übertragung der Kontrolle in den Bereich automatischer Variablen fällt (z. B. durch Vorwärtsspringen über eine Deklaration) Anweisung), das Programm ist schlecht geformt (kann nicht kompiliert werden), es sei denn ... "
idclev 463035818
Fügen Sie das /permissive-Flag zu MSVC hinzu, und es wird sich ebenfalls beschweren. Ich weiß jedoch nicht, ob das Verhalten von MSVC ohne dieses Flag genau definiert ist (ich würde es annehmen, sonst warum sollten sie es zulassen?).
Walnuss
@walnut "sonst warum sollten sie es zulassen" Möglicherweise aus Gründen der Abwärtskompatibilität oder weil ihnen der Standard nicht allzu wichtig ist. Alle gängigen Compiler entsprechen in den Standardeinstellungen nicht dem Standard.
HolyBlackCat
stackoverflow.com/a/7334968/4386278
Asteroiden mit Flügeln

Antworten:

20

Es ist schlecht geformt, wenn die Initialisierung nicht leer ist.

[stmt.dcl]

3 Es ist möglich, in einen Block zu übertragen, jedoch nicht so, dass Deklarationen mit Initialisierung umgangen werden (einschließlich solcher in Bedingungen und Init-Anweisungen). Ein Programm, das von einem Punkt, an dem sich eine Variable mit automatischer Speicherdauer nicht im Gültigkeitsbereich befindet, zu einem Punkt springt, an dem sie sich im Gültigkeitsbereich befindet, ist fehlerhaft, es sei denn, die Variable verfügt über eine leere Initialisierung ([basic.life]). In einem solchen Fall werden die Variablen mit leerer Initialisierung in der Reihenfolge ihrer Deklaration konstruiert.

Der Initialisierer macht die Initialisierung nicht leer. Im Gegensatz dazu

void foo()
{
    goto bar;
    int x; // no initializer
    bar: ;
}

wäre wohlgeformt. Es gelten jedoch die üblichen Einschränkungen bei der Verwendung xmit einem unbestimmten Wert.

Geschichtenerzähler - Unslander Monica
quelle
Müssen Variablendeklarationen nicht sowieso das erste in einem Bereich sein?
Cruncher
4
@ Cruncher - C89 benötigt es. C ++ hat es nie getan und modernes C auch nicht mehr.
Geschichtenerzähler - Unslander Monica
3

Aus der goto-Anweisung :

Wenn die Übertragung der Kontrolle in den Bereich automatischer Variablen gelangt (z. B. durch Vorwärtsspringen über eine Deklarationsanweisung), ist das Programm fehlerhaft (kann nicht kompiliert werden), es sei denn, alle Variablen, deren Bereich eingegeben wird, haben

  1. Skalartypen ohne Initialisierer deklariert
  2. Klassentypen mit trivialen Standardkonstruktoren und trivialen Destruktoren, die ohne Initialisierer deklariert wurden
  3. Lebenslauf-qualifizierte Versionen einer der oben genannten
  4. Arrays von einem der oben genannten
Wahrheitssucher
quelle