Frage

Ich habe dies immer gefragt - warum können Sie nicht Variablen nach einem Fall Etikett in einer switch-Anweisung deklarieren? In C ++ können Sie Variablen ziemlich überall erklären (und sie in der Nähe der ersten Verwendung erklärt ist natürlich eine gute Sache), aber die folgenden immer noch nicht funktionieren:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

Das oben gibt mir die folgenden Fehler (MSC):

  

Initialisierung von 'newVal' wird übersprungen, von 'Fall' Label

Dies scheint auch eine Beschränkung in anderen Sprachen zu sein. Warum ist das so ein Problem?

War es hilfreich?

Lösung

Case Aussagen sind nur Labels . Dies bedeutet, dass der Compiler dies als ein Sprung direkt auf das Etikett interpretiert. In C ++ ist das Problem hier ein Spielraum. Ihre geschweiften Klammern definieren den Umfang als alles in der switch Aussage. Das bedeutet, dass Sie mit einem Umfang gelassen werden, wo ein Sprung wird weiter in den Code ausgeführt werden, um die Initialisierung zu überspringen. Der richtige Weg, dies zu handhaben ist, einen Umfang spezifisch für diese case Anweisung zu definieren und definieren Sie Ihre Variable innerhalb dieser Gruppe.

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}

Andere Tipps

Diese Frage ist wurde ursprünglich als [C] markiert und [C ++] zur gleichen Zeit. Der ursprüngliche Code ist in der Tat ungültig in C und C ++, aber aus ganz anderen Gründen in keinen Zusammenhang. Ich glaube, dieses wichtige Detail wurde verpasst (oder verschleiert) durch die vorhandenen Antworten.

  • In C ++ dieser Code ist ungültig, da das case ANOTHER_VAL: Label in den Anwendungsbereich des Variable newVal springt seine Initialisierung umgangen wird. Springt die Bypass-Initialisierung von lokalen Objekten illegal ist in C ++. Diese Seite des Problems richtig von den meisten Antworten angesprochen wird.

  • jedoch in der Sprache C unter Umgehung Variableninitialisierung ist kein Fehler. Springen in den Geltungsbereich einer Variablen über seine Initialisierung ist legal in C bedeutet es einfach, dass die Variable nicht initialisierten gelassen wird. Der ursprüngliche Code nicht kompiliert aus einem ganz anderen Grunde in C. Label case VAL: im ursprünglichen Code wird auf die Erklärung der variablen newVal befestigt. In der Sprache C Erklärungen sind keine Aussagen. Sie können nicht markiert werden. Und das ist es, was den Fehler verursacht, wenn dieser Code als C-Code interpretiert wird.

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }
    

Hinzufügen eine zusätzliche {} Block Behebungen sowohl C ++ und C Probleme, auch wenn diese Probleme sehr unterschiedlich sein passieren. Auf der Seite ++ C schränkt sie den Umfang der newVal, um sicherzustellen, dass case ANOTHER_VAL: nicht mehr springt in diesen Umfang, die das C ++ Problem beseitigt. Auf der C-Seite, die zusätzliche {} eine zusammengesetzte Anweisung führt, wodurch das case VAL: Etikett auf eine Erklärung anzuwenden, die das C Problem beseitigt.

  • In C Fall kann das Problem leicht ohne {} gelöst werden. Fügen Sie einfach eine leere Anweisung nach dem case VAL: Label und der Code gültig

    switch (val)  
    {  
    case VAL:;            /* Now it works in C! */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:  
      ...
      break;
    }
    

    Beachten Sie, dass, obwohl es jetzt gültig von C Sicht ist es von C ++ Sicht ungültig bleibt.

  • Symmetrisch, in C ++ Fall kann das Problem leicht ohne die {} gelöst werden. Entfernen Sie einfach die initializer von Variablendeklaration und der Code wird gültig

    switch (val)  
    {  
    case VAL: 
      int newVal;
      newVal = 42;  
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }
    

    Beachten Sie, dass, obwohl es jetzt gültig von C ++ Sicht ist es von C Sicht ungültig bleibt.

Ok. Nur um zu klären hat dies strikt nichts mit der Erklärung zu tun. Es bezieht sich nur auf "über die Initialisierung Springen" (ISO C ++ '03 6.7 / 3)

Viele der Beiträge hier haben erwähnt, dass „nicht erklärt zu werden“ über die Erklärung Springen in den Variablen führen. Das ist nicht wahr. Ein POD-Objekt kann ohne initializer deklariert werden, aber es wird einen unbestimmten Wert hat. Zum Beispiel:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

Wenn das Objekt ein nicht-POD oder aggregieren der Compiler fügt implizit einen Initialisierer, und so ist es nicht möglich, über eine solche Erklärung zu springen:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

Diese Einschränkung ist nicht auf die Switch-Anweisung begrenzt. Es ist auch ein Fehler verwenden ‚goto‘ über eine Initialisierung zu springen:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

Ein bisschen Sicht ist, dass dies ein Unterschied zwischen C ++ und C C, es ist kein Fehler, über die Initialisierung zu springen.

Wie andere erwähnt haben, ist die Lösung einen verschachtelten Block hinzufügen, so dass die Lebensdauer der Variablen auf den Einzelfall Etikett begrenzt ist.

Die ganze switch-Anweisung ist im gleichen Umfang. Um es zu bekommen, dies zu tun:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

Hinweis die Klammern.

Nachdem alle Antworten zu lesen und etwas mehr Forschung ich ein paar Dinge bekommen.

Case statements are only 'labels'

C, gemäß der Spezifikation,

§6.8.1 etikettierte Anweisungen:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

In C gibt es keine Klausel, die für eine „bezeichneten Erklärung“ ermöglicht. Es ist einfach nicht Teil der Sprache.

So

case 1: int x=10;
        printf(" x is %d",x);
break;

Das wird nicht kompiliert finden Sie unter http://codepad.org/YiyLQTYw . GCC gibt einen Fehler:

label can only be a part of statement and declaration is not a statement

Selbst

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

Das ist auch nicht kompilieren finden Sie unter http://codepad.org/BXnRD3bu. Hier bin ich auch den gleichen Fehler.


In C ++, gemäß der Spezifikation,

markiert Deklaration ist erlaubt, aber markiert -initialization ist nicht erlaubt.

Siehe http://codepad.org/ZmQ0IyDG .


Lösung zu einem solchen Zustand ist zwei

  1. Verwenden Sie entweder neue Möglichkeiten mit {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
    
  2. oder verwendet Dummy-Anweisung mit dem Label

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
    
  3. Deklarieren Sie die Variable vor dem Schalter () und mit unterschiedlichen Werten in case-Anweisung initialisieren, wenn es Ihre Anforderung erfüllt

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }
    

Einige weitere Dinge mit switch-Anweisung

Sie keine Aussagen in dem Schalter schreiben, die nicht Teil eines Etiketts sind, weil sie nie ausgeführt werden:

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

Siehe http://codepad.org/PA1quYX3 .

Sie können dies nicht tun, weil case Etiketten tatsächlich nur Einspeisepunkten in den umschließenden Block sind.

Dies ist am deutlichsten dargestellt von Duff Gerät . Hier einige Code aus Wikipedia:

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

Beachten Sie, wie die case Etiketten völlig die Blockgrenzen ignorieren. Ja, das ist böse. Aber das ist, warum Ihr Codebeispiel funktioniert nicht. Springen zu einem case Etikett ist das gleiche wie mit goto, so dass Sie nicht springen über eine lokale Variable mit einem Konstruktor erlaubt.

Wie mehrere andere Plakate angegeben haben, müssen Sie in einem Block von Ihrer eigenen stellen:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }

Die meisten der Antworten bisher falsch sind in einer Hinsicht: Sie können deklarieren Variablen nach der Case-Anweisung, aber Sie nicht initialisieren sie:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

Wie bereits erwähnt, ein schöner Weg, um dies Klammern zu verwenden, um einen Rahmen für Ihren Fall zu erstellen.

Mein Liebling böse Schalter Trick ist, ein zu verwenden, wenn (0) ein unerwünschtes Fall Etikett zu überspringen.

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

Aber nur sehr schlecht.

Versuchen Sie folgendes:

switch (val)
{
    case VAL:
    {
        int newVal = 42;
    }
    break;
}

Sie können Variablen innerhalb einer switch-Anweisung deklarieren , wenn Sie einen neuen Block starten:

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

Der Grund ist, mit Zuteilung (und Rückgewinnung) Platz auf dem Stack zur Speicherung der lokalen Variablen (s).

zu tun

Bedenken Sie:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

In Abwesenheit von break-Anweisungen, manchmal newVal wird zweimal erklärt, und man weiß nicht, ob es erst zur Laufzeit der Fall ist. Meine Vermutung ist, dass die Begrenzung wegen dieser Art von Verwirrung ist. Was wäre der Umfang der newVal? Übereinkommen diktieren würde, dass sie die Gesamtheit des Schalterblockes würden (zwischen den Klammern).

Ich bin kein C ++ Programmierer, aber in C:

switch(val) {
    int x;
    case VAL:
        x=1;
}

Adaequat. eine Variable in einem Schalterblock Deklarieren ist in Ordnung. Deklarieren nach einem Fall Wache nicht.

Der gesamte Abschnitt des Schalters ist eine einzige Erklärung Kontext. Sie können eine Variable in einem Fall Aussage wie das erklären. Versuchen Sie stattdessen:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}

Wenn Ihr Code sagt „int newVal = 42“, dann würde man vernünftigerweise erwarten, dass newVal nie uninitialised wird. Aber wenn man diese Aussage lese über (das ist, was Sie tun), dann genau das ist, was passiert, -. NewVal in-scope aber nicht zugewiesen worden

Wenn das ist, was Sie wirklich geschehen soll, um dann die Sprache erfordert, indem sie es explizit machen sagen „int newVal; newVal = 42;“. Andernfalls können Sie den Umfang von newVal auf den Einzelfall begrenzen, was wahrscheinlicher ist, was Sie wollten.

Es kann Dinge klären, wenn Sie das gleiche Beispiel betrachten, sondern mit "const int newVal = 42;"

Ich wollte nur betonen slim 's Punkt . Ein Switch-Konstrukt erzeugt einen ganzen, erstklassigen-Bürger Umfang. So ist es posible (und initialisieren) eine Variable in einer switch-Anweisung vor dem ersten Fall Etikett zu deklarieren, ohne ein zusätzliches klammerten:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}

Bisher haben die Antworten waren für C ++.

Für C ++, können Sie nicht über eine Initialisierung springen. Sie können in C jedoch in C, eine Erklärung ist keine Aussage, und Fall-Etiketten müssen von Anweisungen befolgt werden.

Also, gültig (aber hässlich) C, C ++ ungültig

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

Conversly, in C ++, eine Erklärung ist eine Aussage, so gilt folgendes C ++, C ungültig

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}

Interessant, dass das ist in Ordnung:

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

... aber das ist nicht:

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

Ich, dass ein Fix ist einfach genug, aber ich bin noch nicht verstehen, warum das erste Beispiel des Compiler nicht stört. Wie bereits erwähnt (2 Jahre zuvor hehe), Erklärung ist nicht das, was den Fehler verursacht, auch trotz der Logik. Die Initialisierung ist das Problem. Wenn der Variable initialisiert und auf den verschiedenen Linien erklärt, kompiliert es.

ich diese Antwort schrieb orginally für Frage . Jedoch, wenn ich es fertig fand ich, dass Antwort geschlossen wurde. Also gab ich es hier vielleicht jemand, den Verweis auf Standard mag, wird es hilfreich sein.

Original-Code in Frage:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

Es gibt eigentlich zwei Fragen:

1. Warum kann ich erklären, eine Variable nach case Label?

Es ist, weil in C ++ Etikett in Form sein:

N3337 6.1 / 1

  

markierte Anweisung:

     

...

     
      
  • Attribut-specifier-seqopt case constant-expression: statement
  •   
     

...

Und in C++ Deklarationsanweisung wird auch in Betracht gezogen, wie Aussage (im Gegensatz zu C):

N3337 6/1:

  

Anweisung :

     

...

     

Deklaration-Anweisung

     

...

2. Warum kann ich springen über Variablendeklaration und dann verwenden?

Denn: N3337 6.7 / 3

  

Es ist möglich, in einen Block zu übertragen, , aber nicht in einer Weise, die Erklärungen mit der Initialisierung umgeht. EIN   Programm, das springt   (Der Transfer von die Bedingung einer switch-Anweisung auf einen Fall Etikett betrachtet wird ein Sprung in dieser Hinsicht.)

     

von einem Punkt, an dem eine Variable mit automatischer Speicherdauer bis zu einem Punkt in ihrem Umfang nicht ist, wo es in Rahmen ist, schlecht gebildet es sei denn, die Variable skalare Typen hat , Klassentyp mit einem trivialen Standard   Konstruktor und eine triviale destructor, eine CV-qualifizierte Version von einem dieser Typen, oder eine Anordnung von einer der   vorstehende Typen und wird ohne initializer erklärt (8,5).

Da k von ist skalare Typ , und nicht an dem Punkt der Erklärung springt über sie Erklärung initialisiert ist möglich. Dies ist semantisch äquivalent:

goto label;

int x;

label:
cout << x << endl;

Allerdings wäre das nicht möglich sein, wenn x am Punkt der Deklaration initialisiert wurde:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;

Neue Variablen können nur im Block Umfang decalared werden. Sie müssen so etwas schreiben:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

Natürlich newVal hat nur Umfang innerhalb der geschweiften Klammern ...

Cheers, Ralph

Ein switch Block ist nicht das gleiche wie eine Folge von if/else if Blöcken. Ich bin überrascht, keine andere Antwort erklärt es deutlich.

Betrachten Sie diese switch Anweisung:

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

Es mag überraschend sein, aber der Compiler wird es nicht als einfacher if/else if sehen. Es wird den folgenden Code erzeugen:

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

Die case Anweisungen werden in Etiketten umgewandelt und dann mit goto genannt. Die Klammern erstellen Sie einen neuen Rahmen und es ist einfach, jetzt zu sehen, warum Sie nicht zwei Variablen mit dem gleichen Namen innerhalb eines switch Block deklarieren kann.

Es mag seltsam aussehen, aber es ist notwendig, zu unterstützen fallthrough (das heißt, nicht break mit Ausführung zur nächsten case lassen fortgesetzt).

newVal existiert im gesamten Umfang des Schalters wird aber nur dann, wenn das VAL Glied getroffen wird initialisiert. Wenn Sie einen Block um den Code in VAL erstellen sollte es in Ordnung sein.

C ++ Standard hat: Es ist möglich, in einen Block zu übertragen, aber nicht in einer Weise, die Erklärungen mit der Initialisierung umgeht. Ein Programm, das von einem Punkt springt, wo eine lokale Variable mit der Dauer mit dynamischer Speicher ist in ihrem Umfang nicht bis zu einem Punkt, wo sie in ihrem Umfang ist schlecht gebildet, wenn die variable POD-Typen hat (3.9), und wird ohne Initialisierer (8,5) erklärt.

Der Code, diese Regel zu veranschaulichen:

#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}

Der Code der initializer Wirkung zeigen:

#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}

Ich glaube, die Frage auf der Hand, dass die Aussage war übersprungenen, und man versuchte, die var an anderer Stelle zu verwenden, wäre es nicht deklariert werden.

Es scheint, dass anonyme Objekte können für den Grund in einem Schaltergehäuse Anweisung deklariert oder erstellt werden, dass sie nicht referenziert werden können und als solche nicht bis zum nächsten Fall fallen kann. Betrachten Sie dieses Beispiel kompiliert auf GCC 4.5.3 und Visual Studio 2008 (könnte ein Compliance-Problem sein tho‘so Experten bitte wiegen)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top