Switch deyimi neden bir mola gerektirecek şekilde tasarlandı?


139

Basit bir anahtar bildirimi verildi

switch (int)
{
    case 1 :
    {
        printf("1\n");
        break;
    }

    case 2 : 
    {
        printf("2\n");
    }

    case 3 : 
    {
        printf("3\n");
    }
}

Durum 2'de bir kesme ifadesinin olmaması, yürütmenin durum 3 için kod içinde devam edeceğini ima eder. Bu bir kaza değildir; bu şekilde tasarlandı. Bu kararlar neden verildi? Bu, bloklar için otomatik bir anlam semantikine karşı ne fayda sağlar? Gerekçe neydi?

Yanıtlar:


150

Birçok cevap , ifadeyi gerektirme nedeni olarak düşme yeteneğine odaklanıyor gibi görünmektedir break.

Bunun sadece bir hata olduğuna inanıyorum, çünkü C tasarlandığında bu yapıların nasıl kullanılacağı konusunda neredeyse hiç deneyim yoktu.

Peter Van der Linden "Uzman C Programlama" kitabında dava açıyor:

Varsayılan düşüşün ne sıklıkta kullanıldığını görmek için Sun C derleyici kaynaklarını analiz ettik. Sun ANSI C derleyici ön ucunda her biri ortalama yedi kasa bulunan 244 anahtar ifadesi bulunur. Düşme, tüm bu vakaların sadece% 3'ünde görülür.

Başka bir deyişle, normal anahtar davranışı zamanın% 97'sinde yanlıştır . Sadece bir derleyicide değil - aksine, bu analizde düşüşün kullanıldığı yerlerde, bir derleyicide diğer yazılımlardan daha sık meydana gelen durumlar için, örneğin bir veya iki işlenen içerebilen operatörleri derlerken. :

switch (operator->num_of_operands) {
    case 2: process_operand( operator->operand_2);
              /* FALLTHRU */

    case 1: process_operand( operator->operand_1);
    break;
}

Vaka düşmesi o kadar geniş bir kusur olarak kabul edilir ki, yukarıda gösterilen özel bir yorum sözleşmesi bile vardır, bu da tiftik "bunun gerçekten düşmenin istendiği vakaların% 3'ünden biri olduğunu" söyler.

(Sadece tek bir blok bloğu olduğu sürece) hala birden çok vaka etiketlerinin yığılmasına izin verirken (her durumda bloğunun sonunda açık bir atlama ifadesi gerektiren C # için iyi bir fikir olduğunu düşünüyorum. C # 'da hala bir vakanın diğerine düşmesini sağlayabilirsiniz - sadece düşme yoluyla düşmeyi bir sonraki vakaya atlayarakgoto .

Java'nın C semantiğinden kopma fırsatını almadığı çok kötü.


Gerçekten de, uygulamanın basitliği için gittiğini düşünüyorum. Diğer bazı diller, belki de verimlilik maliyetiyle daha karmaşık vakaları (aralıklar, çoklu değerler, dizeler ...) destekledi.
PhiLho

Java muhtemelen alışkanlıklarını kırmak ve karışıklığı yaymak istemiyordu. Farklı bir davranış için farklı anlambilim kullanmak zorunda kalacaklardı. Java tasarımcıları zaten C'den kopmak için bir dizi fırsatı kaybetti.
PhiLho

@PhiLho - Bence muhtemelen "uygulama basitliği" ile gerçeğe en yakınsınız.
Michael Burr

GOTO yoluyla açık atlamalar kullanıyorsanız, sadece bir dizi IF ifadesi kullanmak kadar verimli değil mi?
DevinB

3
Pascal bile anahtarlarını ara vermeden uygular. C derleyici kodlayıcısı bunu nasıl düşünemezdi @@
Chan Le

30

Birçok yönden c, standart montaj deyimleri için temiz bir arayüzdür. Atlama tablosu tahrikli akış kontrolü yazarken, programcı "kontrol yapısı" ndan düşme veya atlamadan seçim yapma seçeneğine sahiptir ve bir atlama, açık bir talimat gerektirir.

Yani, c de aynı şeyi yapıyor ...


1
Birçok insanın bunu söylediğini biliyorum, ama bence bu tam resim değil; C aynı zamanda, anti-yapıları birleştirmek için genellikle çirkin bir arayüzdür. 1. Çirkin: İfadeler yerine ifadeler. "Beyanname kullanımı yansıtır." Övmek olarak copy-paste modülerlik ve taşınabilirlik için mekanizma. Tür sistem yolu çok karmaşık; böcekleri teşvik eder . . (Bir sonraki yorumda devam ediyor.)NULL
Harrison

2. Antipatterns: Montajın iki temel veri temsil deyiminden biri olan "temiz" etiketli sendikalar sağlamaz. (Knuth, cilt 1, ch 2) Bunun yerine "etiketsiz sendikalar" deyimsel olmayan bir hack. Bu seçim, insanların on yıllardır veriyi düşünme biçimini değiştirdi. Ve NULsonlandırılmış dizeler şimdiye kadarki en kötü fikir.
Harrison

@HarrisonKlaperman: Dizeleri saklama yöntemi mükemmel değildir. Boş sonlandırılmış dizelerle ilişkili sorunların büyük çoğunluğu, dizeleri kabul eden yordamlar da bir maksimum uzunluk parametresini kabul ettiyse oluşmaz ve uzunluk işaretli dizeleri arabellek boyutu parametresi kabul etmeden sabit boyutlu arabelleklere depolayan rutinlerle gerçekleşir. . Vakaların basitçe etiket olduğu switch deyiminin tasarımı modern gözlere garip gelebilir, ancak Fortran DOdöngüsünün tasarımından daha kötü değildir .
supercat

Montajda bir atlama tablosu yazmaya karar verirseniz, büyük / küçük harf değerini alır ve sihirli bir şekilde atlama tablosu aboneliğine dönüştürür, o konuma atlar ve kodu çalıştırırım. Bundan sonra bir sonraki davaya atlamayacağım. Tüm davaların çıkışı olan tek tip bir adrese atlayacağım. Bir sonraki davanın bedenine atlayacağım ya da düşeceğim fikri saçma. Bu model için bir kullanım durumu yoktur.
EvilTeach

2
Günümüzde insanlar kendini ayağa vurmasını önleyen idos ve dil özelliklerini temizlemek ve kendini korumak için daha çok kullanılırken, bu, baytların pahalı olduğu bir alandan bir arınmadır (C 1970'ten önce başladı). Kodunuzun 1024 bayta sığması gerekiyorsa, kod parçalarını yeniden kullanmak için ağır bir hazırlık yaşayacaksınız. Aynı ucu paylaşan farklı giriş noktalarından başlayarak kodu yeniden kullanmak, bunu başarmanın bir mekanizmasıdır.
rpy

23

Duff'ın cihazını uygulamak için açıkçası:

dsend(to, from, count)
char *to, *from;
int 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);
    }
}

3
Duff'ın Cihazını seviyorum. Çok zarif ve hızlı.
dicroce

7
ya, ama SO'da bir switch deyimi her ortaya çıktığında mı ortaya çıkıyor?
billjamesdev

Kapanmış iki kıvırcık parantezinizi kaçırdınız ;-).
Toon Krijthe

18

Olgular örtük olarak kırılmak üzere tasarlanmış olsaydı o zaman atılım olamazdı.

case 0:
case 1:
case 2:
    // all do the same thing.
    break;
case 3:
case 4:
    // do something different.
    break;
default:
    // something else entirely.

Anahtar her durumdan sonra örtük olarak kırılmak üzere tasarlanmış olsaydı, bu konuda bir seçiminiz olmazdı. Şalt kutusu yapısı daha esnek olacak şekilde tasarlanmıştır.


12
Örtük bir şekilde kırılan, ancak "düşme" anahtar kelimesine sahip bir anahtarı görüntüleyebilirsiniz. Garip ama uygulanabilir.
dmckee --- eski moderatör yavru kedi

Bu nasıl daha iyi olurdu? Ben "vaka başına kod bloğu" daha sık bu şekilde çalışmak için bir vaka deyimi hayal ediyorum ... bu bir if / then / else blok.
billjamesdev

Soruda, "case" ifadesinin stiline benzediğinden, her durumda bloktaki {} kapsam kasalarının karışıklığa eklediğini ekliyorum.
Matthew Smith

1
@Bill: Sanırım daha da kötü olurdu, ama Mike B'nin getirdiği şikayeti ele alacaktı: bu düşüş (birden fazla vaka dışında aynı) nadir bir olaydır ve varsayılan davranış olmamalıdır.
dmckee --- eski moderatör yavru kedi

1
Kıskaçları kısalık için bıraktım, birden fazla ifade ile orada olmalılar. Geri dönüşün yalnızca vakalar tamamen aynı olduğunda kullanılması gerektiğine katılıyorum. Olgular daha önceki vakalar üzerinde çalışma kullanarak inşa edildiğinde kafa karıştırıcı bir durum.
Bill the Lizard

15

Bir switch deyimindeki case deyimleri yalnızca etiketlerdir.

Bir değeri açtığınızda, switch ifadesi esasen etikete eşleşen değerle bir goto yapar .

Bu, bir sonraki etiketin altındaki koda geçmemek için kopmanın gerekli olduğu anlamına gelir.

Nedenle gelince neden sonbahar-through switch ifadesinden doğada bazı senaryolarda yararlı olabilir - bu şekilde hayata geçirildi. Örneğin:

case optionA:
    // optionA needs to do its own thing, and also B's thing.
    // Fall-through to optionB afterwards.
    // Its behaviour is a superset of B's.
case optionB:
    // optionB needs to do its own thing
    // Its behaviour is a subset of A's.
    break;
case optionC:
    // optionC is quite independent so it does its own thing.
    break;

2
İki büyük sorun var: 1) breakİhtiyaç duyulan yeri unutmak . 2) Vaka ifadelerinin sıraları değiştiyse, geçiş yanlış vakanın yürütülmesine neden olabilir. Böylece, C # 'ın çok daha iyi işlem goto casegörüyorum ( boş durum etiketleri hariç, geçiş için açık ).
Daniel Rose

@DanielRose: 1) breakC # 'da da unutmanın yolları var - en basit olanı, bir caseşey yapmak istemediğiniz, ancak eklemeyi unutmadığınız zamandır break(belki de açıklayıcı yorumda çok fazla sarıldınız veya diğer görev): yürütme sadece caseaşağıya düşecektir . 2) cesaretlendirmek goto case, yapılandırılmamış "spagetti" kodlamasını cesaretlendiriyor - case A: ... goto case B; case B: ... ; goto case A;özellikle vakalar dosyada ayrıldığında ve birlikte grok yapmak zor olduğunda, yanlışlıkla döngüler ( ) ile sonuçlanabilir . C ++ 'da, geçiş yerelleştirilir.
Tony Delroy

8

Gibi şeylere izin vermek için:

switch(foo) {
case 1:
    /* stuff for case 1 only */
    if (0) {
case 2:
    /* stuff for case 2 only */
    }
    /* stuff for cases 1 and 2 */
case 3:
    /* stuff for cases 1, 2, and 3 */
}

caseAnahtar kelimeyi bir gotoetiket olarak düşünün ve çok daha doğal bir şekilde geliyor.


8
Yani if(0)ilk davanın sonunda kötülük ve objektif kod karartmak için ise yalnızca kullanılmalıdır.
Daniel Rose

11
Bütün bu cevap, kötülük yapma alıştırmasıydı bence. :-)
R .. GitHub BUZA YARDIMCI DURDUR

3

Birkaç durumun aynı kodu (veya sırayla aynı kodu) yürütmesi gerektiğinde kod çoğaltmasını ortadan kaldırır.

Montaj dili düzeyinde, her biri arasında kırılıp kırılmayacağınız umurumda değil, yine de vakalar için düşme için sıfır ek yük var, bu yüzden bazı durumlarda önemli avantajlar sundukları için neden izin vermiyorsunuz?


2

Vektörlerde yapılara değer atama davasına koştum: veri vektörü yapıdaki veri üyelerinin sayısından daha kısa olursa, üyelerin geri kalanı içinde kalacaktı. varsayılan değerleri. Bu durumda ihmal breakoldukça faydalı olmuştur.

switch (nShorts)
{
case 4: frame.leadV1    = shortArray[3];
case 3: frame.leadIII   = shortArray[2];
case 2: frame.leadII    = shortArray[1];
case 1: frame.leadI     = shortArray[0]; break;
default: TS_ASSERT(false);
}

0

Burada belirtildiği gibi, tek bir kod bloğunun birden fazla durumda çalışmasına izin vermek. Bu olmalı , anahtar ifadeleriniz için, örneğinizde belirttiğiniz "vaka başına kod bloğu" ndan daha yaygın bir durum .

Düşme olmadan vaka başına bir kod bloğunuz varsa, belki de daha uygun göründüğü için if-elseif-else bloğunu kullanmayı düşünmelisiniz.

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.