Çalışması gereken bir anahtarda bir vakayı atlamak için if (0) kullanıyor mu?


117

Bir C ++ switch deyimindeki iki durumun her ikisinin de üçüncü bir duruma düşmesini istediğim bir durum var. Özellikle, ikinci vaka üçüncü duruma düşeceği ve ilk vaka da üçüncü duruma düşeceği olmadan ikinci durumda geçerek.

Aptal bir fikrim vardı, denedim ve işe yaradı! Bir ikinci durumda sarılmış if (0) {... }. Şöyle görünüyor:

#ifdef __cplusplus
#  include <cstdio>
#else
#  include <stdio.h>
#endif

int main(void) {
    for (int i = 0; i < 3; i++) {
        printf("%d: ", i);
        switch (i) {
        case 0:
            putchar('a');
            // @fallthrough@
            if (0) {        // fall past all of case 1 (!)
        case 1:
            putchar('b');
            // @fallthrough@
            }
        case 2:
            putchar('c');
            break;
        }
        putchar('\n');
    }
    return 0;
}

Çalıştırdığımda istenen çıktıyı alıyorum:

0: ac
1: bc
2: c

Hem C hem de C ++ 'da denedim (her ikisi de clang ile) ve aynı şeyi yaptı.

Sorularım: Bu C / C ++ geçerli mi? Yaptığı şeyi yapması mı gerekiyor?


33
Evet, bu geçerlidir ve Duff'un cihazının neden yaptığı aynı nedenlerle çalışır .
dxiv

41
Bunun gibi bir kodun, okunabilirlik ve sürdürülebilirlik konusuna birazcık bile önem veren bir ortamda sizi herhangi bir kod adımından attıracağını unutmayın.
Andrew Henle

16
Bu korkunç. Duff'ın cihazından bile daha korkunç, dikkat edin. İlgili, yakın zamanda şöyle bir şey de gördüm switch(x) { case A: case B: do_this(); if(x == B) also_do_that(); ... }. Bu aynı zamanda, IMO, korkunçtu. Lütfen, iki yerde bir satırı tekrarlamanız gerektiği anlamına gelse bile, böyle şeyleri ifadelermiş gibi yazın. Daha sonra yanlışlıkla yalnızca tek bir yerde güncelleme riskini azaltmak için işlevleri ve değişkenleri (ve belgeleri!) Kullanın.
ilkkachu

48
:-) Bu koda baktığı için yaralananlar veya sakatlananlar için bunun iyi bir fikir olduğunu söylemedim . Aslında bunun aptalca bir fikir olduğunu söyledim.
Mark Adler

4
Bunun gibi anahtarların içindeki yapıların RAII ile iyi
oynamadığını unutmayın

Yanıtlar:


57

Evet, buna izin verilir ve istediğinizi yapar. Bir switchifade için, C ++ standardı şunu söylüyor :

durum ve varsayılan etiketler kendi başlarına bu tür etiketlerde engelsiz olarak devam eden kontrol akışını değiştirmez. Bir anahtardan çıkmak için ara bölümüne bakın.

[Not 1: Genellikle, bir anahtarın konusu olan alt ifade bileşik ve durumdur ve varsayılan etiketler (bileşik) alt katmanı içinde yer alan üst düzey ifadelerde görünür, ancak bu gerekli değildir. Bildirimler, bir switch ifadesinin alt ifadesinde görünebilir. - son not]

Dolayısıyla, ififade değerlendirildiğinde, kontrol akışı if, araya giren durum etiketlerinden bağımsız olarak bir ifadenin kurallarına göre ilerler .


13
Bu özel bir durum olarak, bir bakabilirsiniz Boost.Coroutines ++ 03 kurallarını C kullanarak coroutines uygulamak için bu kuralın (ve C ++ yarım düzine köşe durumlarda) yararlanmak mide çalkalama makrolar kümesi için. C ++ 20'ye kadar dilin bir parçası yapılmadılar, ancak bu makrolar onları işe yaradı ... önce antasitlerinizi aldığınız sürece!
Cort Ammon

58

Evet, bunun işe yaraması gerekiyor. C'deki bir switch ifadesinin durum etiketleri neredeyse aynen goto etiketleri gibidir (iç içe geçmiş switch ifadeleriyle nasıl çalıştıkları hakkında bazı uyarılar vardır). Özellikle, "kasanın içinde" olduğunu düşündüğünüz ifadeler için blokları kendileri tanımlamazlar ve bir goto ile yapabileceğiniz gibi bunları bir bloğun ortasına atlamak için kullanabilirsiniz. Bir bloğun ortasına atlarken, goto ile aynı uyarılar, değişkenlerin başlatılmasının üzerine atlamak vb.

Bununla birlikte, pratikte bunu aşağıdaki gibi bir goto ifadesiyle yazmak muhtemelen daha açıktır:

    switch (i) {
    case 0:
        putchar('a');
        goto case2;
    case 1:
        putchar('b');
        // @fallthrough@
    case2:
    case 2:
        putchar('c');
        break;
    }

1
"Bir bloğun ortasına atlarken, goto ile aynı uyarılar, değişkenlerin başlatılmasının üzerinden atlamak vb. İle ilgili olarak geçerlidir." Bunlar ne gibi uyarılar olabilir? Sen olamaz değişkenlerin başlatma üzerinden atlamak.
Kanatlı Asteroitler

4
@AsteroidsWithWings, standartlara uygun bir perspektiften veya bir derleyicinin buna izin vermeyeceği pratik bir perspektiften "olamaz" mı demek istiyorsunuz? Çünkü benim GCC, bir uyarı ile C modunda buna izin veriyor. Yine de C ++ modunda buna izin vermiyor.
ilkkachu

5
@quetzalcoatl gcc-9 ve clang-6'nın her ikisi de bu koda izin verir ve potansiyel olarak başlatılmamış hakkında uyarı verir bee(C modunda; C ++ 'da "anahtar deyiminden bu duruma atlayamazlar / atlama değişken başlatmayı atlar").
Ruslan

7
Daha gototemiz bir çözüm kullanırken yanlış bir şey yaptığınızı biliyorsunuz .
Konrad Rudolph

9
@KonradRudolph: gotoÇok kötü niyetli ama karmaşık kontrol yapılarını manuel olarak yapmıyorsanız, gerçekten sakıncalı bir şey yok. "Zararlı olarak kabul edilen yolun" tamamı, bir derleyici tarafından yayılan asm gibi görünen HLL (örneğin C) kodu yazmakla ilgiliydi (her yerde jmps ileri geri). Goto'nun yalnızca ileri (kavramsal olarak farklı olmayan breakveya erken return), yalnızca olası olmayan yeniden deneme döngüsü (ortak akış yolunu engellemekten kaçınır ve iç içe geçme eksikliğini telafi eder) gibi birçok iyi yapılandırılmış kullanımı vardır continue.
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

29

Diğer cevapların da belirttiği gibi, buna teknik olarak standart tarafından izin verilmektedir, ancak kodun gelecekteki okuyucuları için çok kafa karıştırıcı ve belirsizdir.

Bu nedenle switch ... caseifadeler genellikle çok sayıda satır içi kodla değil işlev çağrılarıyla yazılmalıdır.

switch(i) {
case 0:
    do_zero_case(); do_general_stuff(); break;
case 1:
    do_one_case(); do_general_stuff(); break;
case 2:
    do_general_stuff(); break;
default:
    do_default_not_zero_not_one_not_general_stuff(); break;
}

Sorudaki kodun do_general_stuffvarsayılan olmadığını , yalnızca durum 2 (ve 0 ve 1) için olduğunu unutmayın. Yine ide 0..2 aralığının dışında hiçbir zaman anahtarı çalıştırmaz .
Peter Cordes

@PeterCordes - yanıttaki kodun do_general_stuffvarsayılan olmadığını , yalnızca durum 2 (ve 0 ve 1) için olduğunu unutmayın.
Pete Becker

Bir öneri - belki de varsayılan durum kaldırılmalı veya yeniden adlandırılmalıdır? Farklı işlev adlarını gözden kaçırmak oldukça kolaydır.
I.Am.A.Guy

@PeteBecker: Oh, IDK bunu nasıl özledim generalve defaultfarklı kelimelerdi. OTOH, insanların orta harfler karıştırılsa bile genellikle bir kelimeyi okuyabildiği bilinen bir gerçektir; başlangıç ​​ve bitişe bakma eğilimindeyiz, bu nedenle sadece ortadaki farklı olmak muhtemelen kayma için ideal değildir. Belki do_öneki kaldırın .
Peter Cordes

1
@supercat: Bu "iyi bilinen gerçek" orta harfleri farklı olanlarla değiştirmekle ilgili değil, sadece o zamanın sırasını karıştırmakla ilgili. "Graneel'de cialm doğruysa, sen de haksızsın."
Michael Karcher
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.