Bir case deyiminde {} kullanmak. Neden?


101

Bir ifadede {ve kullanmanın amacı nedir ? Normalde, bir ifadede kaç satır olursa olsun , tüm satırlar çalıştırılır. Bu sadece eski / yeni derleyiciler için bir kural mı yoksa bunun arkasında bir şey mi var?}casecase

int a = 0;
switch (a) {
  case 0:{
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
  }
}

ve

int a = 0;
switch (a) {
  case 0:
    std::cout << "line1\n";
    std::cout << "line2\n";
    break;
}

57
Bir kullanım, case ifadesinde belirtilen değişkenlerin kapsamını sınırlamak olabilir.
Abhishek Bansal


1
Ayrıca çok fazla girinti. Durumlar, yalnızca switch deyiminin bloğundaki etiketlerdir: ek iç içe geçme sağlamazlar, bu nedenle switchanahtar kelimeyle hizalanmaları gerekir ve ikinci örnekte, kapalı ifadeler yalnızca bir kez girintilidir. 'Den sonra nasıl garip bir dört boşluk bıraktığınıza dikkat edin break;.
Kaz

Kabul edilen yanıt, Jack'in yorumunun işaret ettiği ve cevabımda değindiğim bazı incelikleri gözden kaçırdığı için kısmen doğrudur .
Shafik Yaghmour

Bilginize: C ++ yerine C (hatta C11) olarak, bir bildirimi etiketleyemezsiniz; sözdizimsel kategoride değillerdir statement. C ++, siz (sözdizimsel kategoriden sadece bir bileşeni olabilir statementolduğunu declaration statement).
Jonathan Leffler

Yanıtlar:


195

{}Yeni bir bloğu belirtmektedir kapsamı .

Aşağıdaki çok uydurma örneği düşünün:

switch (a)
{
    case 42:
        int x = GetSomeValue();
        return a * x;
    case 1337:
        int x = GetSomeOtherValue(); //ERROR
        return a * x;
}

xKapsamda zaten tanımlandığı için bir derleyici hatası alırsınız .

Bunları kendi alt kapsamlarına ayırmak x, switch ifadesinin dışında bildirim yapma ihtiyacını ortadan kaldıracaktır .

switch (a)
{
    case 42: {
        int x = GetSomeValue();
        return a * x; 
    }
    case 1337: {
        int x = GetSomeOtherValue(); //OK
        return a * x; 
    }
}

11
Aslında IMO, x değişkeninin ikinci bildirimini atlasanız bile bir derleyici hatası alırsınız.
Abhishek Bansal

1
Her ne kadar bu stili aşırı kullanmak ve switch ifadesinin içine büyük bloklar koymak, vakaları takip etmeyi okunamaz hale getirecektir. İfadeleri küçük tutmayı tercih ederim.
masoud

2
@MatthieuM. MS Visual Studio 2010'un Abhishek'ın belirttiği davranışa sahip olacağını biliyorum: bir durum içinde herhangi bir değişken bildirimini derlemeyecektir (bu durumda yeni bir kapsamı belirtmek için süslü parantez kullanmadığınız sürece). Bunun standartlara uyup uymadığını bilmiyorum.
KRyan

1
@KRyan: öyle değil, ama bu o kadar güvenli bir alternatif ki, bunu uyguladıkları için onları suçlayamam.
Matthieu M.

6
Standardın 6.7 (3) Bölümü (2005 taslağı için numaralandırma), bir başlatmayı atlayamayacağınızı, bu nedenle bir vaka bloğunda başlatmaya sahip olamayacağınızı belirtir.
Jack Aidley

23

TL; DR

Bir vaka içinde başlatıcı veya önemsiz olmayan bir nesneye sahip bir değişken tanımlamanın tek yolu, bir döngü veya if ifadesi gibi kendi kapsamına sahip bir blok kapsamını{} veya başka bir kontrol yapısını kullanmaktır. .

Kanlı ayrıntılar

Durumların , bir goto ifadesiyle kullanılan etiketler gibi yalnızca etiketli ifadeler olduğunu görebiliriz ( bu, C ++ taslak standart bölüm 6.1 Etiketli ifadede ele alınmıştır ) ve 3. paragrafta , birçok durumda beyannameyi geçmeye izin verilmediğini görebiliriz. , başlatmaya sahip olanlar dahil:6.7

Bir bloğa transfer etmek mümkündür, ancak başlatma ile bildirimleri atlayacak şekilde değil. Atlayan bir program 87 otomatik depolama süresine sahip bir değişken o kapsamda bir noktaya kapsamında olmayan bir noktadan, değişken önemsiz bir varsayılan yapıcı ve önemsiz bir yıkıcının ile skaler tip, sınıf türüne sahip olmadıkça kötü şekillendirilmiş olan bu türlerden birinin cv nitelikli bir sürümü veya önceki türlerden birinin bir dizisi ve bir başlatıcı olmadan bildirilir (8.5).

ve şu örneği sağlar:

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
}

Unutmayın, burada bazı incelikler vardır, başlatma içermeyen skaler bir bildirimi atlamanıza izin verilir , örneğin:

switch( n ) 
{
    int x ;
    //int x  = 10 ; 
    case 0:
      x = 0 ;
      break;
    case 1:
      x = 1 ;
      break;
    default:
      x = 100 ;
      break ;
}

tamamen geçerlidir ( canlı örnek ). Elbette, her durumda aynı değişkeni beyan etmek istiyorsanız, o zaman her birinin kendi kapsamına ihtiyacı olacaktır, ancak switch ifadelerinin dışında da aynı şekilde çalışır , bu yüzden bu büyük bir sürpriz olmamalıdır.

Başlangıca atlamaya izin vermeme gerekçesine gelince, kusur raporu 467 , biraz farklı bir konuyu kapsasa da otomatik değişkenler için makul bir durum sağlar :

[...] otomatik değişkenler, açık bir şekilde başlatılmamışsa, tuzak gösterimleri de dahil olmak üzere belirsiz ("çöp") değerlere sahip olabilir, [...]

Bir içinde kapsamını genişletmek nerede durumda bakmak için muhtemelen daha ilginç anahtarı çoklu üzerinde vakaların bu en ünlü örnekleri muhtemelen Duff aygıtı şöyle görünecektir:

void send( int *to, const int *from, int  count)
{
        int n = (count + 7) / 8;
        switch(count % 8) 
        {
            case 0: do {    *to = *from++;   // <- Scope start
            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);    // <- Scope end
        }
}

6

Değişken bildirimlerini sonuçta ortaya çıkan yıkıcı (veya kapsam çatışmaları) ile birlikte casecümlelere enjekte etmenize izin veren bir alışkanlıktır . Buna bakmanın bir başka yolu da, tüm akış kontrolünün ifade dizilerinden değil bloklardan oluştuğu, diledikleri dil için yazıyor olmalarıdır.


4

Bunu temel bir derleyici kısıtlaması olarak kontrol edin ve neler olduğunu merak etmeye başlayacaksınız:

int c;
c=1;

switch(c)
{
    case 1:
    //{
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    //}

    default : cout<<"def";
}

Bu size bir hata verecektir:

error: jump to case label [-fpermissive]
error:   crosses initialization of int* i

Bu olmayacak olsa da:

int c;
c=1;

switch(c)
{
    case 1:
    {
        int *i = new int;
        *i =1;
        cout<<*i;
        break;
    }

    default : cout<<"def";
}

1

Anahtarda parantez kullanmak, Rotem'in söylediği gibi yeni bir kapsam bloğunu belirtir.

Ancak okuduğunuzda netlik açısından da olabilir. Koşullu bir kırılma yaşayabileceğiniz için davanın nerede durduğunu bilmek.


0

Sebepler şunlar olabilir:

  1. Okunabilirlik, kapsamlı bir bölüm olarak her durumu görsel olarak geliştirir.
  2. Birkaç anahtar durumu için aynı ada sahip farklı bir değişken bildirmek.
  3. Mikro optimizasyonlar - vakanın kapsamından çıkar çıkmaz yok etmek istediğiniz, gerçekten pahalı bir kaynak tahsisli değişkenin kapsamı veya hatta "GOTO" komut kullanımının daha spagetti senaryosu.
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.