[c++] Warum können Variablen in einer switch-Anweisung nicht deklariert werden?



Answers

Diese Frage wird gleichzeitig als [C] und [C ++] markiert. Der ursprüngliche Code ist tatsächlich sowohl in C als auch in C ++ ungültig, aber aus völlig anderen Gründen. Ich glaube, dieses wichtige Detail wurde durch die vorhandenen Antworten verfehlt (oder verschleiert).

  • In C ++ ist dieser Code ungültig, weil der case ANOTHER_VAL: label newVal Umgehung seiner Initialisierung in den Gültigkeitsbereich der Variablen newVal springt. Sprünge, die die Initialisierung von lokalen Objekten umgehen, sind in C ++ illegal. Diese Seite des Problems wird von den meisten Antworten korrekt behandelt.

  • In C-Sprache ist das Umgehen der Variableninitialisierung jedoch kein Fehler. Das Springen in den Gültigkeitsbereich einer Variablen über ihre Initialisierung ist in C erlaubt. Das bedeutet einfach, dass die Variable nicht initialisiert ist. Der ursprüngliche Code kompiliert nicht in C aus einem ganz anderen Grund. Label- case VAL: im ursprünglichen Code ist an die Deklaration der Variablen newVal . In C sind Sprachdeklarationen keine Aussagen. Sie können nicht etikettiert werden. Und das verursacht den Fehler, 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;
    }
    

Das Hinzufügen eines zusätzlichen {} -Blocks behebt sowohl C ++ - als auch C-Probleme, obwohl diese Probleme sehr unterschiedlich sind. Auf der C ++ - Seite beschränkt es den Geltungsbereich von newVal und stellt sicher, dass die case ANOTHER_VAL: nicht mehr in diesen Bereich springt, wodurch das C ++ - Problem beseitigt wird. Auf der C-Seite führt das Extra {} eine zusammengesetzte Anweisung ein, wodurch die case VAL: label auf eine Anweisung angewendet wird, wodurch das C-Problem beseitigt wird.

  • In C kann das Problem ohne das {} leicht gelöst werden. Fügen Sie einfach eine leere Anweisung nach dem case VAL: label hinzu und der Code wird 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 aus Sicht von C gültig ist, aus Sicht von C ++ ungültig bleibt.

  • Symmetrisch, in C ++ - Fall kann das Problem ohne das {} leicht gelöst werden. Entfernen Sie einfach den Initialisierer aus der 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 aus Sicht von C ++ gültig ist, es aus Sicht von C ungültig bleibt.

Question

Ich habe mich immer gefragt, warum Sie Variablen nicht nach einer Case-Bezeichnung in einer switch-Anweisung deklarieren können. In C ++ können Sie Variablen ziemlich überall deklarieren (und es ist offensichtlich eine gute Sache, sie nahe der Erstbenutzung zu deklarieren), aber Folgendes wird immer noch nicht funktionieren:

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

Das obige gibt mir den folgenden Fehler (MSC):

Die Initialisierung von 'newVal' wird über die 'case'-Bezeichnung übersprungen

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




Interessant, dass das in Ordnung ist:

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 verstehe, dass eine Korrektur einfach genug ist, aber ich verstehe noch nicht, warum das erste Beispiel den Compiler nicht stört. Wie bereits erwähnt (2 Jahre zuvor hehe), ist die Deklaration nicht der Grund für den Fehler, trotz der Logik. Initialisierung ist das Problem. Wenn die Variable in den verschiedenen Zeilen initialisiert und deklariert ist, wird sie kompiliert.




Ich glaube, das Problem ist, dass die Anweisung übersprungen wurde, und Sie versucht haben, die var anderswo zu verwenden, würde es nicht erklärt werden.




newVal existiert im gesamten Umfang des Schalters, wird aber nur initialisiert, wenn der VAL-Schenkel berührt wird. Wenn Sie einen Block um den Code in VAL erstellen, sollte es OK sein.




Neue Variablen können nur im Blockbereich dekaliert werden. Sie müssen etwas wie folgt schreiben:

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

Natürlich hat newVal nur Spielraum innerhalb der Klammern ...

Prost, Ralph




Mein liebster böser Switch-Trick besteht darin, ein if (0) zu verwenden, um ein unerwünschtes Case-Label zu überspringen.

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

Aber sehr böse.




Die gesamte switch-Anweisung ist im selben Umfang. Um es zu umgehen, tun Sie Folgendes:

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

    case ANOTHER_VAL:
      ...
    break;
}

Beachten Sie die Klammern.




Der gesamte Abschnitt des Schalters ist ein einzelner Deklarationskontext. Sie können eine Variable in einer solchen Case-Anweisung nicht deklarieren. Versuchen Sie es stattdessen:

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



Ich wollte nur den Slim - point betonen. Ein Switch-Konstrukt erzeugt einen vollständigen First-Class-Citizen-Bereich. So ist es möglich, eine Variable in einer switch-Anweisung vor der ersten Case-Bezeichnung zu deklarieren (und zu initialisieren), ohne ein zusätzliches Klammerpaar:

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



Erwägen:

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

In Abwesenheit von Break-Anweisungen wird manchmal newVal zweimal deklariert, und Sie wissen nicht, ob es bis zur Laufzeit funktioniert. Meine Vermutung ist, dass die Beschränkung auf diese Art von Verwirrung zurückzuführen ist. Was wäre der Umfang von newVal? Konvention würde diktieren, dass es der gesamte Schalterblock (zwischen den Klammern) wäre.

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

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

Funktioniert gut. Das Deklarieren einer Variablen in einem Switch-Block ist in Ordnung. Deklarieren nach einem Fallschutz ist nicht.




Dies ist nicht möglich, da case Labels eigentlich nur Einstiegspunkte in den umschließenden Block sind.

Dies wird am deutlichsten durch Duffs Gerät veranschaulicht. Hier ist ein Code von 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, dass die case Labels die Blockgrenzen vollständig ignorieren. Ja, das ist böse. Aus diesem Grund funktioniert das Codebeispiel nicht. Das Springen zu einer Fallbezeichnung ist dasselbe wie das Verwenden von goto , so dass es nicht erlaubt ist, mit einem Konstruktor über eine lokale Variable zu springen.

Wie einige andere Plakate angedeutet haben, müssen Sie einen eigenen Block setzen:

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



Related