Neden tehlikelisin?
goto
kendi başına kararsızlığa neden olmaz. Yaklaşık 100.000 goto
saniyeye rağmen , Linux çekirdeği hala bir istikrar modelidir.
goto
kendi başına güvenlik açıklarına neden olmamalıdır. Ancak bazı dillerde, try
/ catch
istisna yönetim bloklarıyla karıştırılması , bu CERT tavsiyesinde açıklandığı gibi güvenlik açıklarına neden olabilir . Mainstream C ++ derleyicileri bu hataları işaretler ve önler, ancak ne yazık ki, eski veya daha fazla egzotik derleyiciler yok.
goto
okunamayan ve sürdürülemez kodlara neden olur. Buna spagetti kodu da denir , çünkü spagetti tabağında olduğu gibi çok fazla goto olduğunda kontrol akışını takip etmek çok zordur.
Spagetti kodundan kaçınmayı başarabilseniz ve sadece birkaç tane goto kullanıyorsanız bile, bunlar gibi kaynakları ve sızıntıyı kolaylaştırır:
- Açık programlanmış bloklar ve döngüler veya anahtarlarla yapı programlamayı kullanan kodun izlenmesi kolaydır; kontrol akışı çok tahmin edilebilir. Bu nedenle değişmezlere saygı duyulmasını sağlamak daha kolaydır.
- Bir
goto
açıklama ile, bu basit akışı ve beklentileri kırarsınız. Örneğin, hala kaynakları serbest bırakmadığınızı fark etmeyebilirsiniz.
goto
Farklı yerlerdeki birçok kişi sizi tek bir hedef hedefe gönderebilir. Bu nedenle, bu yere ulaştığınızda içinde bulunduğunuz durumun kesin olarak bilinmesi açık değildir. Yanlış / temelsiz varsayımlarda bulunma riski bu nedenle oldukça büyüktür.
Ek bilgi ve teklifler:
C, sınırsızca kötüye kullanılabilir goto
ifadeyi ve etiketlenecek etiketleri sağlar. Resmen goto
hiçbir zaman gerekli değildir ve pratikte hemen hemen her zaman onsuz kod yazmak kolaydır. (...)
Yine de, insanın bir yer bulabileceği birkaç durum önereceğiz. En yaygın kullanım, derhal iç içe geçmiş bazı yapılarda işlemden vazgeçmek, örneğin iki döngüden bir kerede çıkmak. (...)
Her ne kadar konu hakkında dogmatik olmasak da, ifadelerin hiç olmadığı kadar az kullanılması gerektiği görülüyor .
James Gosling ve Henry McGilton 1995 Java dili ortamı beyaz bülteninde yazdı :
Daha Fazla Goto İfadesi Yok
Java'nın hiç bir ifadesi yoktur. Çalışmalar goto'nun (yanlış) basitçe “çünkü orada” olduğundan daha sık kullanıldığını göstermiştir. Goto'yu ortadan kaldırmak, dilin sadeleştirilmesine yol açtı (...) Yaklaşık 100.000 C kod satırı üzerinde yapılan çalışmalar, goto ifadelerinin kabaca yüzde 90'ının yalnızca yuvalanmış ilmeklerden çıkma etkisini elde etmek için kullanıldığını belirledi. Yukarıda da değinildiği gibi, çok seviyeli kırılma ve devam etme ifadeleri için ihtiyaçların çoğunu ortadan kaldırmaya devam edin.
Bjarne Stroustrup , sözlüğünde goto'yu şu davetkar terimlerle tanımlar:
Goto - rezil goto. Öncelikle makinede üretilen C ++ kodunda faydalı.
Ne zaman kullanılabilir?
K & R gibi ben gotos hakkında dogmatik değilim. İnsanın hayatını kolaylaştıracağı durumlar olduğunu itiraf ediyorum.
Tipik olarak, C'de goto, çok seviyeli döngü çıkışına veya şimdiye kadar tahsis edilen tüm kaynakları serbest bırakan / kilidini açan uygun bir çıkış noktasına ulaşmayı gerektiren hata işlemlerine izin verir (sırayla birden fazla etiket, birden fazla etiket anlamına gelir). Bu makale , Linux çekirdeğindeki goto'nun farklı kullanımlarını ölçmektedir.
Şahsen bundan kaçınmayı tercih ediyorum ve 10 yılda C, maksimum 10 goto kullandım. if
Sanırım daha okunaklı olduğunu düşündüğüm iç içe geçmeyi tercih ediyorum . Bu, çok derin bir yuvalamaya neden olduğunda, işlevimi daha küçük parçalara ayırmayı ya da kaskadda bir boolean göstergesi kullanmayı tercih ederim. Günümüzün optimize eden derleyicileri, aynı kodla neredeyse aynı kodu üretecek kadar akıllıdır goto
.
Goto'nun kullanımı yoğun olarak dile bağlıdır:
C ++ 'da, RAII'nin doğru kullanılması derleyicinin kapsam dışına çıkan nesneleri otomatik olarak imha etmesine neden olur, böylece kaynaklar / kilit yine de temizlenecek ve artık gerçek bir ihtiyaç olmayacak.
Java'da hiçbir Goto ihtiyacı (Java'nın yukarıdaki yazar teklifi ve bunu görmek var mükemmel yığın taşması cevap ): karışıklık, temizler çöp toplayıcısı break
, continue
ve try
/ catch
istisna işleme tüm dava kapsayacak goto
yararlı olabilir, ama içinde daha güvenli ve iyi tavır. Java’nın popülaritesi, modern bir dilde deyimden kaçınılabileceğini kanıtlıyor.
Ünlü SSL goto fail güvenlik açığı üzerinde yakınlaştırma
Önemli Sorumluluk Reddi: yorumlardaki şiddetli tartışmalar ışığında, goto ifadesinin bu hatanın tek nedeni olduğunu iddia etmediğimi açıklığa kavuşturmak istiyorum. Yapmadan hiçbir hata olmayacağını sanmıyorum. Sadece bir goto'nun ciddi bir böceğe karıştığını göstermek istiyorum.
goto
Programlama tarihinde kaç tane ciddi hatanın bulunduğunu bilmiyorum : ayrıntılar genellikle iletilmez. Ancak iOS'un güvenliğini zayıflatan ünlü bir Apple SSL hatası vardı. Bu hataya yol açan goto
ifade yanlış bir ifadeydi.
Bazıları, hatanın kök nedeninin kendi başındaki ifade ifadesi değil, hatalı bir kopyala / yapıştır, yanıltıcı bir girinti, koşullu bloğun etrafındaki kıvrık kaşlı ayraçlar veya geliştiricinin çalışma alışkanlıkları olduğunu iddia ediyor. Hiçbirini onaylayamıyorum: tüm bu argümanlar olası hipotezler ve yorumlardır. Gerçekten kimse bilmiyor. ( bu arada, yorumlarda birinin önerdiği gibi yanlış giden bir birleştirme hipotezi, aynı fonksiyondaki diğer girinti tutarsızlıkları açısından çok iyi bir aday gibi görünüyor ).
Tek nesnel gerçek, kopyalananın goto
işlevden erken çıkmasına yol açtığıdır. Kurallara bakıldığında, aynı etkiye neden olabilecek diğer tek açıklama bir dönüş olacaktır.
Hata işlevinde olduğu SSLEncodeSignedServerKeyExchange()
içinde bu dosya :
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)
goto fail;
if ((err =...) !=0)
goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail; // <====OUCH: INDENTATION MISLEADS: THIS IS UNCONDITIONDAL!!
if (...)
goto fail;
... // Do some cryptographic operations here
fail:
... // Free resources to process error
Nitekim, koşullu bloğun etrafındaki küme parantezleri hatayı önleyebilirdi:
ya derleme sırasında bir sözdizimi hatası (ve dolayısıyla bir düzeltme) ya da yedekli zararsız bir başa çıkma yol açardı. Bu arada, GCC 6 tutarsız girintileri tespit etmek için isteğe bağlı uyarısı sayesinde bu hataları tespit edebilecektir.
Fakat ilk etapta, tüm bu gotos daha yapısal bir kodla önlenebilirdi. Yani goto en azından dolaylı olarak bu hatanın bir nedenidir. Bundan kaçınabilecek en az iki farklı yol var:
Yaklaşım 1: fıkra veya iç içe geçmişse if
s
Ardışık hata için birçok koşulu test etmek yerine ve fail
sorun olması durumunda bir etikete her gönderimde , kriptografik işlemlerin if
yalnızca yanlış bir ön koşul olmasaydı yapacak bir açıklamada yürütülmesi istenebilirdi:
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) == 0 &&
(err = ...) == 0 ) &&
(err = ReadyHash(&SSLHashSHA1, &hashCtx)) == 0) &&
...
(err = ...) == 0 ) )
{
... // Do some cryptographic operations here
}
... // Free resources
Yaklaşım 2: bir hata biriktiricisi kullanın
Bu yaklaşım, buradaki hemen hemen tüm ifadelerin bir err
hata kodu belirlemek için bazı işlevler çağırması ve kodun geri kalan kısmını yalnızca err
0 olması durumunda yürütmesi (yani, hatasız çalıştırılan işlev) temeline dayanır . Güzel, güvenli ve okunabilir bir alternatif:
bool ok = true;
ok = ok && (err = ReadyHash(&SSLHashSHA1, &hashCtx))) == 0;
ok = ok && (err = NextFunction(...)) == 0;
...
ok = ok && (err = ...) == 0;
... // Free resources
Burada tek bir goto yoktur: başarısızlık çıkış noktasına hızlıca atlamak için risk yoktur. Ve görsel olarak yanlış hizalanmış bir çizgiyi veya unutulmuş bir çizgiyi bulmak kolay olurdu ok &&
.
Bu yapı daha kompakt. C'de bir mantığın ve ( &&
) ikinci bölümünün sadece ilk bölüm geçerliyse değerlendirilmesi gerçeğine dayanmaktadır . Aslında, bir optimize edici derleyici tarafından üretilen montajcı, gotos'lu orijinal koda hemen hemen eşdeğerdir: Optimizer, koşul zincirini çok iyi algılar ve ilk boş olmayan geri dönüş değerinin sonuna ( çevrimiçi kanıt ) atlayan kod üretir .
Test aşamasında, tamam bayrağı ile hata kodu arasındaki uyuşmazlıkları tanımlayabilecek fonksiyonun sonunda bir tutarlılık kontrolü bile öngörebilirsiniz.
assert( (ok==false && err!=0) || (ok==true && err==0) );
Bu tür bir yanlışlıkların ==0
yanlışlıkla ile değiştirilmesi !=0
veya mantıksal bağlantı hatalarının kolayca ayıklama aşamasında tespit edilecektir.
Dediğim gibi: Alternatif yapıların herhangi bir hatadan kaçınılması gerektiğini iddia etmiyorum. Sadece, böceğin gerçekleşmesini zorlaştırabileceğini söylemek istiyorum.