Buradaki cevaplara eklemek için, bununla birlikte zıt soruyu düşünmeye değer olduğunu düşünüyorum, yani. C neden ilk etapta düşmeye izin verdi?
Herhangi bir programlama dili elbette iki amaca hizmet eder:
- Bilgisayara talimatlar verin.
- Programlayıcının niyetlerinin bir kaydını bırakın.
Herhangi bir programlama dilinin oluşturulması, bu iki amaca en iyi nasıl hizmet edileceği arasında bir dengedir. Bir yandan, bilgisayar yönergelerine (bunlar makine kodu, IL gibi bayt kodu veya yürütme sırasında talimatlar yorumlanıp dönüştürülmemesine) ne kadar kolaysa, derleme veya yorumlama sürecinin verimli, güvenilir ve kompakt çıktı. En uç noktaya gelindiğinde, bu hedef derleme, IL ve hatta ham op kodlarında sadece yazmamızla sonuçlanır, çünkü en kolay derleme hiç derlemenin olmadığı yerdir.
Tersine, dil, programcının amacını ne kadar ifade ederse, bu amaçla alınan araçlardan ziyade, hem yazarken hem de bakım sırasında programı daha anlaşılır hale getirir.
Şimdi, switch
her zaman eşdeğer if-else
blok zincirine veya benzerine dönüştürülerek derlenebilirdi , ancak bir kişinin bir değer aldığı, ondan bir ofset hesapladığı (bir tabloya bakarak olsun) belirli bir ortak montaj desenine derlemeye izin vermek olarak tasarlandı. değerin mükemmel bir karmasıyla veya * değerindeki gerçek aritmetikle dizine eklenir). Bu noktada, C # derlemesinin bazen switch
eşdeğer hale geleceği if-else
ve bazen karma tabanlı bir atlama yaklaşımı (ve benzer şekilde C, C ++ ve benzer sözdizimine sahip diğer diller) kullanacağını belirtmek gerekir .
Bu durumda düşmeye izin vermenin iki iyi nedeni vardır:
Her neyse doğal olarak gerçekleşir: bir talimatlar kümesine bir atlama tablosu oluşturursanız ve daha önceki talimat gruplarından biri bir tür atlama veya geri dönüş içermiyorsa, yürütme doğal olarak bir sonraki partiye ilerleyecektir. Düşenlere izin vermek, switch
-ullanıcı C'yi makine kodunu kullanarak atlama tablosuna dönüştürürseniz "gerçekleşecekti" idi .
Montajda yazan kodlayıcılar eşdeğeri için zaten kullanılmışlardı: montajda elle bir atlama tablosu yazarken, belirli bir kod bloğunun bir dönüşle mi yoksa masanın dışına atlayarak mı yoksa devam edip etmeyeceğini düşünmek zorunda kalacaklardı. sonraki bloğa. Bu nedenle, kodlayıcının break
gerektiğinde açık bir şekilde eklenmesi kodlayıcı için de "doğal" dır.
O zamanlar, hem üretilen makine kodu hem de kaynak kodun ifade edilebilirliği ile ilgili olduğu için, bir bilgisayar dilinin iki hedefini dengelemek için makul bir girişimdi.
Ancak kırk yıl sonra, birkaç nedenden ötürü işler tamamen aynı değildir:
- Bugün C'deki kodlayıcıların montaj deneyimi çok az olabilir veya hiç olmayabilir. Diğer birçok C stili dilde kodlayıcıların (özellikle Javascript!) "İnsanların toplantıdan ne alışkın oldukları" kavramı artık geçerli değildir.
- Optimizasyonlardaki gelişmeler,
switch
ya if-else
yaklaşımın en verimli olacağı düşünülen ya da atlama tablosu yaklaşımının özellikle ezoterik bir varyantına dönüştüğü için dönüştürülme olasılığının daha yüksek olduğu anlamına gelir. Üst ve alt düzey yaklaşımlar arasındaki haritalama eskisi kadar güçlü değildir.
- Deneyimler, düşüşün normdan ziyade azınlık durumu olma eğiliminde olduğunu göstermiştir (Sun'ın derleyicisinin yaptığı bir çalışmada,
switch
blokların % 3'ünün aynı blokta birden çok etiketten başka bir düşme kullandığını ve Buradaki durum, bu% 3'ün aslında normalden çok daha yüksek olduğu anlamına geliyordu). Bu nedenle, çalışılan dil, olağandışı olanı ortak olandan daha kolay bir şekilde hazır hale getirir.
- Deneyimler, düşmenin hem kazayla yapıldığı durumlarda hem de kodu koruyan biri tarafından doğru düşmenin kaçırıldığı durumlarda sorun kaynağı olduğunu göstermiştir. Bu sonuncusu, düşme ile ilişkili hatalara ince bir ektir, çünkü kodunuz mükemmel bir şekilde hatasız olsa bile, düşme yine de sorunlara neden olabilir.
Bu son iki nokta ile ilgili olarak, mevcut K&R sürümünden şu alıntıyı göz önünde bulundurun:
Bir durumdan diğerine düşmek sağlam değildir, program değiştirildiğinde parçalanmaya eğilimlidir. Tek bir hesaplama için birden fazla etiket haricinde, düşüşler az kullanılmalı ve yorumlanmalıdır.
İyi bir form olarak, mantıksal olarak gereksiz olsa da, son durumdan sonra (burada varsayılan) bir mola verin. Bir gün sonunda başka bir vaka eklendiğinde, bu savunma programı biraz sizi kurtaracak.
Bu nedenle, atın ağzından, C'ye düşme problemlidir. Düşüşleri her zaman yorumlarla belgelemek iyi bir uygulama olarak kabul edilir, bu, kişinin olağandışı bir şeyi nerede yapması gerektiğini belgelemesi gereken genel prensibin bir uygulamasıdır, çünkü kodun daha sonra incelenmesini ve / veya kodunuzun buna benzemesini sağlar. aslında doğru olduğunda bir acemi hatası var.
Ve bunu düşündüğünüzde şöyle kodlayın:
switch(x)
{
case 1:
foo();
/* FALLTHRU */
case 2:
bar();
break;
}
Kodda geçişi açık yapmak için bir şey ekliyor, derleyici tarafından tespit edilebilen (veya yokluğu tespit edilebilen) bir şey değil.
Bu nedenle, C # 'da düşme ile açık olması gerektiği gerçeği, diğer C stili dillerinde iyi yazmış olan insanlara, düşme durumlarında zaten açık olacakları için herhangi bir ceza eklemez. †
Son olarak, goto
burada kullanımı zaten C ve diğer dillerden bir normdur:
switch(x)
{
case 0:
case 1:
case 2:
foo();
goto below_six;
case 3:
bar();
goto below_six;
case 4:
baz();
/* FALLTHRU */
case 5:
below_six:
qux();
break;
default:
quux();
}
Bir önceki bloğa bir tane getiren değerden başka bir değer için yürütülen koda bir bloğun dahil edilmesini istediğimiz bu durumda, zaten kullanmak zorundayız goto
. (Elbette, farklı koşullarla bundan kaçınmanın yolları ve yolları vardır, ancak bu, bu soruyla ilgili her şey için geçerlidir). Böyle bir C #, bir kodda birden fazla kod bloğuna çarpmak istediğimiz bir durumla başa çıkmak için zaten normal bir yol üzerine inşa edildi switch
ve sadece düşmeyi de kapsayacak şekilde genelleştirdi. Ayrıca, her iki durumu da daha rahat ve kendi kendini belgelendirdi, çünkü C'ye yeni bir etiket eklememiz gerekiyor, ancak case
C #'da etiket olarak kullanabiliyoruz . C # 'da below_six
etiketten kurtulabiliriz ve goto case 5
ne yaptığımız konusunda daha net olanı kullanabiliriz . (Ayrıca eklemek zorundayızbreak
için, default
sadece yukarıdaki C kodunu açıkça C # kodu yapmak için bıraktım).
Özet olarak bu nedenle:
- C # artık doğrudan C kodunun 40 yıl önce yaptığı gibi (ve bu günlerde C de) optimize edilmemiş derleyici çıktısı ile ilgili değildir, bu da düşme ilhamlarından birini ilgisiz kılar.
- C #
break
, benzer dillere aşina olanlar tarafından dili daha kolay öğrenmek ve daha kolay taşıma için yalnızca örtük olmakla kalmaz .
- C #, son kırk yıldır sorunlara neden olduğu iyi belgelenmiş olası bir hata kaynağını veya yanlış anlaşılmış kodu kaldırır.
- C #, derleyici tarafından C (belge düşme) ile mevcut en iyi uygulamayı yapar.
- C # alışılmadık durumu daha açık kodlu olanı yapar, normal durum kodu olan kişi otomatik olarak yazar.
- C #, C'de kullanıldığı gibi
goto
farklı case
etiketlerden aynı bloğa vurmak için aynı temelli yaklaşımı kullanır. Sadece bazı diğer durumlarda genelleştirir.
- C #, ifadelerin etiket olarak hareket
goto
etmesine izin vererek , bu temelli yaklaşımı C'den daha rahat ve net hale getirir case
.
Sonuçta, oldukça makul bir tasarım kararı
* BASIC'in bazı formları, GOTO (x AND 7) * 50 + 240
kırılgan ve dolayısıyla yasaklamak için özellikle ikna edici bir durum olsa da , beğenilerinizi yapmanıza izin verir goto
, daha düşük seviyeli kodun manuel olarak sürdürülmesi gereken bir şeyden ziyade derlemenin sonucu olduğunda çok daha makul olan bir değer üzerine aritmetik. Özellikle Duff'ın Cihazının uygulamaları, eşdeğer makine koduna veya IL'ye iyi bir şekilde katkıda bulunur, çünkü her talimat bloğu, nop
dolgu maddelerinin eklenmesine gerek kalmadan genellikle aynı uzunlukta olacaktır .
† Duff's Device yine makul bir istisna olarak karşımıza çıkıyor. Bu ve benzer örüntülerle operasyonların tekrarı olması, düşme kullanımını bu etkiye açık bir yorum yapmasa bile nispeten açık hale getirmeye yarar.