Değişkenler neden bir switch deyiminde bildirilemiyor?


944

Bunu hep merak ettim - neden bir switch deyimindeki bir vaka etiketinden sonra değişkenleri açıklayamıyorsunuz? C ++ 'da değişkenleri hemen hemen her yerde bildirebilirsiniz (ve ilk kullanıma yakın olarak bildirmek açıkçası iyi bir şeydir), ancak aşağıdakiler yine de işe yaramaz:

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

Yukarıdaki bana aşağıdaki hatayı (MSC) verir:

'newVal' öğesinin başlatılması 'case' etiketi ile atlanır

Bu diğer dillerde de bir sınırlama gibi görünüyor. Bu neden böyle bir sorun?


10
C BNF dilbilgisine dayalı bir açıklama için bkz. Stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/…
johne

Genel olarak anahtar ifadeleri ve etiketler (ABC :) hakkında gerçekten iyi bir okuma .
Etherealone

4
'Değişkenler neden bildirilmektense bir anahtar deyiminde başlatılamıyor' diyebilirim.
Zoomin

Yanıtlar:


1141

Caseifadeler yalnızca etiketlerdir . Bu, derleyicinin bunu doğrudan etikete atlamak olarak yorumlayacağı anlamına gelir. C ++, burada sorun kapsam biridir. Kıvırcık parantezleriniz kapsamı switchifadenin içindeki her şey olarak tanımlar . Bu, başlatmayı atlayan koda daha fazla atlamanın gerçekleştirileceği bir kapsamda kaldığınız anlamına gelir.

Bunu işlemenin doğru yolu, söz konusu caseifadeye özgü bir kapsam tanımlamak ve içindeki değişkeninizi tanımlamaktır:

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

94
Yeni bir kapsam açma ile ilgili - kodda okunabilirlik ve tutarlılığı destekleyin. Eski günlerde, otomatik olarak bir "ekstra" yığın çerçeveniz olabilir, ancak şimdi herhangi bir iyi optimize edici derleyici için durum böyle olmamalıdır.
Uzun Boylu Jeff

10
Jeff ile hemfikirim - çoğu insanın kullandığı girinti tarzı nedeniyle bir anahtar ifadesini okurken kapsamı "varsaymak" çok kolaydır. Kendi tarzım, birden fazla satır uzunluğunda her durum / varsayılan için her zaman yeni bir kapsam açmaktır.
Teklifler

39
workmad3 - Herhangi bir yeni değişken bildirmezseniz bana yeni bir yığın çerçeve oluşturacak herhangi bir C ++ derleyicisi bulabilir misiniz? Kısa bir süre beni endişelendirdiniz, ancak G ++ 3.1, Visual C ++ 7 veya Intel C ++ 8'in hiçbiri, herhangi bir değişken bildirmediğiniz yeni kapsamlar için herhangi bir kod üretmeyecek.
Chris Jefferson

10
@ workmad3 yeni bir küme parantez bloğu girerek yeni bir yığın çerçevesine neden olmaz stackoverflow.com/questions/2759371/…
MTVS

3
@TallJef Hangi 'eski günlerden' bahsettiğinizi bilmiyorum. Bir yöntem için yığın alanı bir derleyici karşılaştı asla değildir yöntem 40 yıl içinde, girildiğinde ayırdı.
user207421

331

Bu soru, bir tarafından [C] ve aynı zamanda [C ++] olarak etiketlendi. Orijinal kod hem C hem de C ++ için gerçekten geçersizdir, ancak tamamen farklı ilgisiz nedenlerden dolayı.

  • C ++ 'da bu kod geçersizdir çünkü case ANOTHER_VAL:etiket newValbaşlatılmasını atlayarak değişkenin kapsamına atlar . Otomatik nesnelerin başlatılmasını atlayan atlamalar C ++ 'da geçersizdir. Sorunun bu tarafı çoğu cevap tarafından doğru bir şekilde ele alınmaktadır.

  • Bununla birlikte, C dilinde değişken başlatmayı atlamak bir hata değildir. Bir değişkenin başlangıç ​​durumuna getirilmesi kapsamına atlamak C'de yasaldır. Bu basitçe değişkenin başlatılmadan bırakıldığı anlamına gelir. Orijinal kod tamamen farklı bir nedenle C'de derlenmez. case VAL:Orijinal koddaki etiket , değişken bildirimine eklenir newVal. C dilinde bildirimler ifade değildir. Etiketlenemezler. Ve bu kod C kodu olarak yorumlandığında hataya neden olan şey budur.

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

Ekstra bir {}blok eklemek , bu sorunlar çok farklı olmasına rağmen hem C ++ hem de C sorunlarını düzeltir. C ++ tarafında newVal, case ANOTHER_VAL:artık C ++ sorununu ortadan kaldıran bu kapsama atlanmadığından emin olarak kapsamını kısıtlar . C tarafında ekstra {}bir bileşik ifadeyi tanıtır, böylece case VAL:etiketi C sorununu ortadan kaldıran bir ifadeye uygulamak için yapar.

  • C durumunda sorun kolayca çözülebilir {}. case VAL:Etiketin ardından boş bir ifade eklemeniz yeterlidir ve kod geçerli olur

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

    Şimdi C açısından geçerli olsa da, C ++ bakış açısından geçersiz kaldığını unutmayın.

  • Simetrik olarak, C ++ durumunda sorun olmadan kolayca çözülebilir {}. Başlatıcıyı değişken bildirimden kaldırmanız yeterlidir ve kod geçerli olur

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

    Şimdi C ++ bakış açısından geçerli olsa da, C bakış açısından geçersiz kaldığını unutmayın.


4
@AnT: Neden C ++ düzelten C için geçerli olmadığını anlıyorum; Ancak, ilk etapta başlangıç ​​atlama atlamak C ++ sorunu nasıl düzeltebilir anlayamıyorum? Yine ilanını ve atama atlamak olmaz newValo atlar zaman ANOTHER_VAL?
legends2k

13
@ legends2k: Evet, hala atlıyor. Ancak, "sorunu giderir" dediğimde , C ++ derleyici hatasını düzelttiği anlamına gelir . C ++ 'da başlatıcı ile bir skaler bildirimi atlamak yasadışıdır , ancak başlatıcı olmadan bir skaler bildirimi atlamak mükemmeldir . En case ANOTHER_VAL:alanına değişken newValancak belirsiz değer ile görülebilir.
AnT

3
Büyüleyici. Bu soruyu §A9.3: Compound StatementK&R C'den okuduktan sonra buldum (ikinci baskı). Giriş, bir teknik tanımı söz bileşiği ile ifadesi olduğunu {declaration-list[opt] statement-list[opt]}. Ben bir deklarasyon açıklamada WAS düşünmüştü çünkü Confused, ben baktım ve hemen bu soruyu, eşitsizliğin belirgin hale gelir ve aslında söyleyen bir örnek bulduk kırar bir program. Başka bir çözümün (C için) , etiketli ifadenin tatmin olması için deklarasyondan önce başka bir ifade (muhtemelen null bir ifade?) Koymak olacağına inanıyorum .
Braden Best

Hata! Önerdiğim null-ifade çözümünün zaten cevabınızda olduğunu fark ettim. Boş ver o zaman.
Braden Best

3
Boş bir ifade ekleme düzeltmesinin yalnızca C99 için geçerli olduğunu belirtmek gerekir. C89'da, değişkenler kapalı bloklarının başında bildirilmelidir.
Arthur Tacca

136

Tamam. Sadece bunun açıklığa kavuşturmak için bildirgeyle hiçbir ilgisi yoktur. Sadece "başlatmanın üzerinden atlamak" ile ilgilidir (ISO C ++ '03 6.7 / 3)

Buradaki yazıların çoğu, beyanın üzerinden atlamanın değişkenin "beyan edilmemesine" neden olabileceğini belirtti. Bu doğru değil. Bir POD nesnesi başlatıcı olmadan bildirilebilir, ancak belirsiz bir değere sahip olacaktır. Örneğin:

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;
}

Nesnenin POD olmayan veya toplandığı durumlarda derleyici örtük olarak bir başlatıcı ekler ve bu nedenle böyle bir bildirimin üzerinden atlamak mümkün değildir:

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;
}

Bu sınırlama, switch ifadesiyle sınırlı değildir. Bir başlatmanın üzerine atlamak için 'git' kullanmak da bir hatadır:

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

Biraz trivia, bu C ++ ve C arasındaki bir farktır. C'de, başlatma işleminin üzerinden atlamak bir hata değildir.

Diğerlerinin de belirttiği gibi, çözüm, değişkenin ömrünün ayrı ayrı vaka etiketiyle sınırlı olması için iç içe bir blok eklemektir.


2
"Başlatma sırasında atlama hatası" ??? GCC'imle değil. Etiketin altındaki j kullanılırken "j birim olarak kullanılabilir" uyarısı verebilir, ancak hata yoktur. Bununla birlikte, anahtar durumunda bir hata vardır (sert bir hata, zayıf bir uyarı değil).
Mecki

9
@Mecki: C ++ 'da yasa dışıdır. ISO C ++ '03 - 6.7 / 3: "... Otomatik depolama süresine sahip bir yerel değişkenin kapsam dışında olmadığı bir noktadan, değişkenin POD tipi olmadığı sürece kapsamda olduğu bir noktaya atlayan bir program (3.9) ve bir başlatıcı (8.5) olmadan bildirildi. "
Richard Corden

1
Evet, ancak C'de yasadışı değil (en azından gcc bunun olmadığını söylüyor). j başlatılmaz (rastgele bir sayıya sahip), ancak derleyici bunu derler. Ancak, switch deyimi durumunda, derleyici bile derlemez ve bir goto / label durumu ve switch kutusu arasındaki farkı göremiyorum.
Mecki

8
@Mecki: Genel olarak, tek bir derleyici davranışı dilin gerçekte izin vermiş olduğunu yansıtmaz. Hem C'90 hem de C'99'u kontrol ettim ve her iki standart da bir switch deyiminde başlatma üzerinden atlama ile bir örnek içeriyor.
Richard Corden

38

Tüm switch ifadesi aynı kapsamdadır. Etrafında dolaşmak için şunu yapın:

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

    case ANOTHER_VAL:
      ...
    break;
}

Not parantez.


30

Tüm cevapları ve biraz daha araştırmayı okuduktan sonra birkaç şey elde ederim.

Case statements are only 'labels'

C'de, spesifikasyona göre,

§6.8.1 Etiketli İfadeler:

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

C dilinde "etiketli beyan" yapılmasına izin veren herhangi bir madde yoktur. Sadece dilin bir parçası değil.

Yani

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

Bu derlenmeyecektir , bkz. Http://codepad.org/YiyLQTYw . GCC bir hata veriyor:

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

Hatta

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

bu da derlenmiyor , bkz. http://codepad.org/BXnRD3bu . Burada da aynı hatayı alıyorum.


C ++ 'da, spesifikasyona göre,

etiketli bildirime izin verilir, ancak etiketli-başlatmaya izin verilmez.

Bkz. Http://codepad.org/ZmQ0IyDG .


Böyle bir duruma çözüm iki

  1. {} Kullanarak yeni kapsam kullanın

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
  2. Veya etiketli kukla ifade kullanın

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. Switch () yönteminden önce değişkeni bildirin ve gereksiniminizi karşıladığında, case deyiminde farklı değerlerle başlatın

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

Switch deyimiyle ilgili bazı şeyler

Anahtarda hiçbir zaman etiketin parçası olmayan hiçbir ifade yazmayın, çünkü asla yürütülmezler:

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

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

    default:
        break;
}

Bkz. Http://codepad.org/PA1quYX3 .


2
C sorununu doğru bir şekilde tanımladınız. Ancak C ++ etiketli başlatmaya izin verilmediği iddiası tamamen doğru değildir. C ++ etiketli başlatma ile ilgili yanlış bir şey yok. C ++ 'ın izin vermediği, değişkenin başlatılması üzerine değişken kapsamına atlamaktır . Yani, C açısından, problem etiketle ilgilidir ve doğru bir şekilde tarif ettiniz. Ancak C ++ açısından sorun etiketle ilgilidir. aacase VAL:case ANOTHER_VAL:
AnT

C ++ 'da, C' den farklı olarak, bildirimler ifadelerin bir alt kümesidir.
Keith Thompson

20

Bunu yapamazsınız, çünkü caseetiketler aslında sadece içeren bloğa giriş noktalarıdır.

Bu, Duff'ın cihazı tarafından en açık şekilde gösterilmiştir . İşte Wikipedia'dan bazı kodlar:

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);
    }
}

caseEtiketlerin blok sınırlarını nasıl yok saydığına dikkat edin . Evet, bu kötülük. Ancak bu nedenle kod örneğiniz çalışmaz. Bir caseetikete gotoatlamak, kullanmakla aynıdır , bu nedenle bir kurucu ile yerel bir değişkenin üzerinden atlamanıza izin verilmez.

Diğer birkaç afişin de belirttiği gibi, kendi bloğunuzu koymanız gerekir:

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

1
Bu Duff'ın cihaz uygulamasında onu son derece yavaşlatan bir hata var: count int türü, bu nedenle%, gerçek bir bölme / modulo işlemi gerçekleştirmelidir. Sayımı imzasız yapın (veya daha iyisi, sayımlar / dizinler için her zaman size_t kullanın) ve sorun ortadan kalkar.
R .. GitHub DURDURMA BUZA YARDIM ET

1
@R ..: Ne ?! Bir ikisinin tamamlayıcı sisteminde, imza 2 modlu güçlerle modülleri etkilemez (sadece alt bitlerde bir AND'dir) ve işlemci mimariniz aritmetik sağ kaydırma işlemi olduğu sürece 2'lik güçlere bölünmeleri etkilemez. ( SARx86'da, SHRimzasız vardiyalara karşılık).
Chris Jester-Young

@Chris: Derleyicinin "yalnızca alt bitlerde bir AND" ifadesinin bulunmadığı negatif değerlere izin vermesi gerektiği anlamına geldiğine inanıyorum; örneğin, -1% 8, g ++ kullanarak bu ikinin tamamlayıcı sisteminde -1 verir (bu durumda işaret 5.6 / 4 için tanımlanan bir uygulamadır).

3
@Chris: R'nin etkiyi abarttığına katılıyorum; Ben sadece yorumunu gördüm ve basit olduğunu biliyordum VE yeterli değildi.

1
Ayrıca dikkat edilmesi gereken orijinal Wikipedia kodu, burada belirtilmeyen ve her baytın aynı "konumuna" kopyalandığı için burada garip görünen bir bellek eşlemeli çıkışa veri göndermek içindir. Bunun için postfix ++ ekleyerek veya kullanım örneğinin bellek eşlemeli G / Ç için olduğunu belirterek bu sorunu çözebiliriz. Tamamen orijinal soru için periferik :-).
Peter

16

Yanıtların çoğu bugüne kadar bir açıdan yanlıştır: Eğer yapabilirsiniz case ifadesi sonrası değişkenleri bildirmek, ancak edemez onları başlatmaya:

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

Daha önce de belirtildiği gibi, bunun iyi bir yolu davanız için bir kapsam oluşturmak için parantez kullanmaktır.


1
Bay 32, hatanızın ne olduğunu yanlış anladınız: evet bu derlenmeyecek, ancak bir anahtarın içinde bir değişken bildirdiğiniz için değil. Hata, C'de yasadışı olan bir ifadeden sonra bir değişken bildirmeye çalışmanızdır.
MrZebra

1
Şimdi c90'da yasal olan bir gün ve c'nin daha yeni versiyonu
Jeegar Patel

12

En sevdiğim kötü anahtar hilesi, istenmeyen bir vaka etiketini atlamak için bir if (0) kullanmaktır.

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

Ama çok kötü.


Çok hoş.
Nedeni

1
Hem durum 0'ın hem de durum 1'in durum 2'ye düşmesini istiyorsanız (vaka 0 durum 1'e düşmeden). Gerçekten yararlı olup olmadığını bilmiyorum, ama kesinlikle işe yarıyor.
Petruza

1
gotoKodun
gizlenmesi

10

Bunu dene:

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

7

Bir anahtar ifadesi içinde değişkenleri ilan edebilir eğer yeni bir blok başlatın:

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

Bunun nedeni, yerel değişken (ler) in depolanması için yığın üzerinde alan tahsis edilmesi (ve geri kazanılması) ile ilgilidir.


1
Değişken bildirilebilir, ancak başlatılamaz. Ayrıca, sorunun zaten yığın ve yerel değişkenlerle ilgili olmadığından eminim.
Richard Corden

6

Düşünmek:

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

Break ifadelerinin yokluğunda, bazen newVal iki kez bildirilir ve çalışma zamanına kadar çalışıp çalışmadığını bilmezsiniz. Tahminimce bu sınırlama bu tür bir karışıklıktan kaynaklanıyor. NewVal'ın kapsamı ne olurdu? Sözleşme, tüm anahtar bloğunun (parantezler arasında) olacağını belirleyecektir.

Ben hiçbir C ++ programcısı değilim, ama C:

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

İyi çalışıyor. Bir anahtar bloğunun içindeki bir değişkenin bildirilmesi uygundur. Vaka nöbetinden sonra ilan etmek değildir.


3
@ Mr.32: aslında örneğiniz bir printf yürütülmediğini gösterir, ancak bu durumda int x bir ifade değil, bir bildirimdir, x bildirilir, işlev ortamı her istiflendiğinde bunun için yer ayrılır, bkz: codepad.org/4E9Zuz1e
Petruza

Sorunun başlığını okurken bunu bulmayı bekliyordum, çünkü soru "case:" etiketleri içinde değişkenleri bildirmekle ilgili değil, anahtar ifadelerinde. Ve sadece siz (ve VictorH, cevabınızı vurgulayarak) anahtar ifadelerindeki değişkenler hakkında konuştunuz.
18'de cesss

4

Anahtarın tüm bölümü tek bir bildirim içeriğidir. Böyle bir vaka deyiminde bir değişkeni bildiremezsiniz. Bunun yerine şunu deneyin:

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

Değişken bildirilebilir, ancak başlatılamaz.
Richard Corden

@Richard Corden Başlamanın işe yarayacağından eminim. Hala başlatılamayacağını iddia ediyor musunuz?
chux - Monica'yı eski durumuna getir

3

Kodunuzda "int newVal = 42" yazıyorsa, newVal'ın hiçbir zaman başlatılmamasını beklersiniz. Ancak bu ifadeyi (eğer yaptığınız şey) ele alırsanız, tam olarak böyle olur - newVal kapsam dahilindedir ancak atanmamıştır.

Eğer gerçekten bunu yapmak istiyorsan, o zaman dilin "int newVal; newVal = 42;" diyerek açık olmasını gerektirir. Aksi takdirde, newVal'ın kapsamını tek vaka ile sınırlayabilirsiniz, bu da istediğiniz şeydir.

Aynı örneği düşünürseniz, ancak "const int newVal = 42;"


3

Sadece vurgulamak istediğim ince 'nin noktası . Bir anahtar yapısı, tüm birinci sınıf vatandaş kapsamını oluşturur. Bu nedenle, bir anahtar deyiminde bir değişkeni ek bir parantez çifti olmadan ilk durum etiketinden önce bildirmek (ve başlatmak) mümkündür :

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

Burada -1 int newVal = 42; asla idam edilemez. bu codepad.org/PA1quYX3
Jeegar Patel

4
beyan int newVal edecektir idam değil edilmesi = 42atama.
Petruza

3

Şimdiye kadar cevaplar C ++ için olmuştur.

C ++ için, bir başlatmanın üzerine atlayamazsınız. C dilinde yapabilirsiniz. Bununla birlikte, C'de bir bildirim bir ifade değildir ve vaka etiketlerinin ardından ifadeler gelmelidir.

Yani, geçerli (ama çirkin) C, geçersiz C ++

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();
}

Tersine, C ++ 'da bir bildirim bir deyimdir, bu nedenle aşağıdakiler geçerli C ++, geçersiz C

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

1
İkinci örnek geçerli DEĞİL C ++ (vc2010 ve gcc ile test 4.6.1 C ++ başlatma bölümünü atlamaya izin vermiyor. Gcc hata mesajı: 'int i'
zhaorufei

3

Bunun iyi olması ilginç:

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

case 1:  
    break;
}

... ama bu değil:

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

case 1:  
    break;
}

Bir düzeltmenin yeterince basit olduğunu anlıyorum, ancak henüz ilk örneğin neden derleyiciyi rahatsız etmediğini anlamıyorum. Daha önce de belirtildiği gibi (2 yıl önce hehe), mantığa rağmen hataya neden olan beyan değildir. Başlatma sorun. Değişken farklı satırlarda başlatılır ve bildirilirse derlenir.


1
Birincisi gcc 4.2'de iyi değil: "error: 'int' öncesi beklenen ifade". Peter ve Mr.32'nin dediği gibi "case 0:; int j; ..." ve "case 0:; int j = 7; ..." her ikisi de işe yarar. C'deki sorun sadece "case <label>: declaration" ifadesinin geçerli C sözdizimi olmamasıdır.
dubiousjim

3

Bu cevabı orjinal olarak bu soru için yazdım . Ancak bitirdiğimde cevabın kapalı olduğunu gördüm. Buraya gönderdim, belki standart referansları seven biri bunu yararlı bulabilir.

Söz konusu Orijinal Kod:

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

Aslında 2 soru var:

1. Neden caseetiketten sonra bir değişken bildirebilirim ?

Çünkü C ++ etiketinde form olması gerekir:

N3337 6.1 / 1

etiketli-deyim:

...

  • özellik-belirteç-seqopt case constant-expression :statement

...

Ve C++ beyanda ayrıca (aksine ) beyan olarak kabul edilir C:

N3337 6/1:

beyan :

...

beyan-ifadesi

...

2. Neden değişken beyanı atlayıp sonra kullanabilirim?

Çünkü: N3337 6.7 / 3

Bir bloğa aktarmak mümkündür, ancak başlatmalarla bildirimleri atlayacak şekilde değil . Atlayan bir program ( Bir switch ifadesinin durumundan bir vaka etiketine transfer, bu açıdan bir sıçrama olarak kabul edilir .)

otomatik depolama süresine sahip bir değişkenin kapsamda olmadığı bir noktadan , değişkenin skaler tipine , önemsiz bir varsayılan yapıcıya sahip sınıf tipine ve önemsiz bir yıkıcıya, cv nitelikli bir versiyona sahip olmadığı sürece kapsamın oluşturulduğu bir noktaya kadar Bu türlerden birinin veya önceki türlerden birinin bir dizi ve bir başlatıcı (8.5) olmadan bildirilir.

Yana ktaşımaktadır skaler tipi ve 's beyanı mümkündür üzerinde beyan atlama noktasında başlatılmadı. Bu anlamsal olarak eşdeğerdir:

goto label;

int x;

label:
cout << x << endl;

Ancak x, beyan noktasında başlatılmış olsaydı , bu mümkün olmazdı :

 goto label;

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

    label:
    cout << x << endl;

1

Yeni değişkenler sadece blok kapsamında bildirilebilir. Bunun gibi bir şey yazmanız gerekiyor:

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

Tabii ki, newVal sadece parantez içinde kapsam var ...

Şerefe Ralph


1

Bir switchblok , ardışık if/else ifbloklarla aynı değildir . Başka hiçbir cevabın net bir şekilde açıklamamasına şaşırdım.

Bu switchifadeyi düşünün :

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

Şaşırtıcı olabilir, ancak derleyici bunu basit olarak görmeyecektir if/else if. Aşağıdaki kodu üretecektir:

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

caseİfadeleri etiketlere dönüştürülür ve daha sonra adı verilir goto. Köşeli ayraçlar yeni bir kapsam oluşturur ve neden bir switchblok içinde aynı ada sahip iki değişkeni bildiremeyeceğinizi görmek kolaydır .

Tuhaf görünebilir, ancak düşmeyi desteklemek gerekir (yani, breakyürütmenin bir sonrakine devam etmesine izin vermek için kullanılmaz case).


0

Elimdeki konu, ifadenin atlanmış olması ve var olanı başka bir yerde kullanmaya çalıştığınız, beyan edilmeyeceğine inanıyorum.


0

newVal, anahtarın tüm kapsamında bulunur ancak yalnızca VAL uzuvuna çarpıldığında başlatılır. Eğer VAL içinde kod etrafında bir blok oluşturursanız, bu OK olmalıdır


0

C ++ Standardı: Bir bloğa aktarılabilir, ancak başlatmalarla bildirimleri atlayacak şekilde değil. Otomatik depolama süresine sahip bir yerel değişkenin kapsam dışında olmadığı bir noktadan, değişkenin POD tipine (3.9) sahip olmadığı ve bir başlatıcı (8.5) olmadan bildirilmediği sürece, kapsam dahilinde olduğu bir noktaya atlayan bir program.

Bu kuralı gösteren kod:

#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;
}

Başlatıcı efektini gösteren kod:

#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;
}

0

Anonim nesnelere , başvurulanamayacakları için bir anahtar durum ifadesinde bildirilebileceği veya oluşturulabileceği ve bu nedenle bir sonraki duruma geçilemeyeceği görülmektedir. Bu örneği GCC 4.5.3 ve Visual Studio 2008 üzerinde derleyin (uzmanlar lütfen tartışın)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

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

    default:
        break;
    }
    return EXIT_SUCCESS;
}

Eğer oy verecekseniz lütfen nedenini açıklayın. Anonim bir nesne oluşturmanın neden bir muafiyet gibi göründüğünü merak ediyorum.
Olumide

1
DV değil, ama: Sorunun tamamı adlandırılmış değişkenlerin bildirilmesi / kapsamı ile ilgilidir. Geçici ("anonim nesne" bir terim değildir) adlandırılmış bir değişken değildir, bir bildirim değildir ve kapsama da tabi değildir ( constkendi kapsamıyla bir referansa bağlı olmadıkça ). Bu, ifadesinde (nerede olursa olsun) yaşayan ve ölen bir ifadedir. Bu nedenle, tamamen alakasız.
underscore_d

Foo();bir deklarasyon değildir; soru beyanlarla ilgilidir.
MM
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.