Was sind die Anzeichen für eine Kreuzinitialisierung?

83

Betrachten Sie den folgenden Code:

#include <iostream>
using namespace std;

int main()
{
    int x, y, i;
    cin >> x >> y >> i;
    switch(i) {
        case 1:
            // int r = x + y; -- OK
            int r = 1; // Failed to Compile
            cout << r;
            break;
        case 2:
            r = x - y;
            cout << r;
            break;
    };
}

G ++ beschwert sich crosses initialization of 'int r'. Meine Fragen sind:

  1. Was ist crosses initialization?
  2. Warum besteht der erste Initialisierer x + ydie Kompilierung, letzterer ist jedoch fehlgeschlagen?
  3. Was sind die Probleme der sogenannten crosses initialization?

Ich weiß, dass ich Klammern verwenden sollte, um den Umfang von anzugeben r, aber ich möchte wissen, warum, zum Beispiel, warum Nicht-POD nicht in einer Switch-Anweisung mit mehreren Fällen definiert werden konnte.

Jichao
quelle
1
Angesichts der folgenden Antworten für Punkt 3 verstehe ich, dass dieser Fehler eine übermäßige Einschränkung von c ++ darstellt. Wenn r nach dem Label nicht verwendet wird, gibt es keine Auswirkungen (selbst wenn das Beispiel hier r verwendet, kann es in Fall 2 entfernt werden und der Compiler würde den gleichen Fehler ausgeben). Der bessere Beweis ist, dass es in C und sogar in C11 erlaubt ist.
Calandoa
Mögliches Duplikat des Fehlers: Zum
Falletikett springen

Antworten:

95

Die Version mit int r = x + y;wird auch nicht kompiliert.

Das Problem ist, dass es möglich ist r, zum Gültigkeitsbereich zu gelangen, ohne dass der Initialisierer ausgeführt wird. Der Code würde gut kompiliert, wenn Sie den Initialisierer vollständig entfernen würden (dh die Zeile würde lesen int r;).

Das Beste, was Sie tun können, ist, den Umfang der Variablen zu begrenzen. Auf diese Weise werden Sie sowohl den Compiler als auch den Leser zufriedenstellen.

switch(i)
{
case 1:
    {
        int r = 1;
        cout << r;
    }
    break;
case 2:
    {
        int r = x - y;
        cout << r;
    }
    break;
};

Der Standard sagt (6.7 / 3):

Es ist möglich, in einen Block zu übertragen, jedoch nicht so, dass Deklarationen bei der Initialisierung umgangen werden. Ein Programm, das von einem Punkt, an dem sich eine lokale 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 hat den POD-Typ (3.9) und wird ohne Initialisierer (8.5) deklariert.

Avakar
quelle
Aber mein G ++ erlaubt es int r = x + y.
Jichao
10
Nun, mein g ++ nicht. Überprüfen Sie den Compiler erneut oder aktualisieren Sie ihn.
Avakar
danke, das war hilfreich für mich. Ich denke, dass der C-Compiler nicht einmal zulässt, dass die Deklaration nach einem Code kommt. Anscheinend erlaubt C99 das aber ... stackoverflow.com/questions/7859424/…
m-ric
36

Sie sollten den Inhalt der casein Klammern setzen, um ihm den Gültigkeitsbereich zu geben. Auf diese Weise können Sie lokale Variablen darin deklarieren:

switch(i) {
    case 1:
        {
            // int r = x + y; -- OK
            int r = 1; // Failed to Compile
            cout << r;
        }
        break;
    case 2:
        ...
        break;
};
Péter Török
quelle
3

Es ist möglich, in einen Block zu übertragen, jedoch nicht so, dass Deklarationen bei der Initialisierung umgangen werden. Ein Programm, das von einem Punkt, an dem sich eine lokale 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 hat den POD-Typ und wird ohne Initialisierer deklariert.

[Example: Code:

void f()
{
  // ...
  goto lx;    // ill-formed: jump into scope of `a'
  // ...
 ly:
    X a = 1;
  // ...
 lx:
   goto ly;    // ok, jump implies destructor
 // call for `a' followed by construction
 // again immediately following label ly
}

--end example]

Die Übertragung von der Bedingung einer switch-Anweisung auf eine Fallbezeichnung wird in dieser Hinsicht als Sprung angesehen.

Ashish Yadav
quelle
1
Willkommen bei Stack Overflow. Sie sollten Quellen für Ihre Zitate angeben, dies ist C ++ 03: 6.7 / 3. Es ist auch der gleiche Absatz, den ich in meiner Antwort zitiert habe.
Avakar
0

Ich schlage vor, Sie fördern Ihre rVariable vor der switchAnweisung. Wenn Sie eine Variable blockübergreifend verwenden möchten case(oder denselben Variablennamen, aber unterschiedliche Verwendungen), definieren Sie sie vor der switch-Anweisung:

#include <iostream>
using namespace std;

int main()
{
    int x, y, i;
    cin >> x >> y >> i;
// Define the variable before the switch.
    int r;
    switch(i) {
        case 1:
            r = x + y
            cout << r;
            break;
        case 2:
            r = x - y;
            cout << r;
            break;
    };
}

Einer der Vorteile besteht darin, dass der Compiler nicht in jedem Block eine lokale Zuordnung (auch bekannt als Push auf den Stapel) durchführen muss case.

Ein Nachteil dieses Ansatzes besteht darin, dass Fälle in andere Fälle "fallen" (dh ohne Verwendung break), da die Variable einen vorherigen Wert hat.

Thomas Matthews
quelle
2
Ich würde vorschlagen, es umgekehrt zu machen. Der Compiler muss in beiden Fällen keine "lokale Zuordnung" durchführen (und in der Praxis auch nicht).
Avakar