Diğerlerinin de söylediği gibi, problemin gotokendisi değil ; Sorun insanların nasıl kullandığı gotove kodun anlaşılmasını ve sürdürülmesini nasıl zorlaştıracağı ile ilgilidir.
Aşağıdaki kod parçasını varsayalım:
i = 4;
label: printf( "%d\n", i );
Hangi değer için yazdırılır i? Ne zaman basılıyor? İşlevindeki her örneği hesaba katana kadar goto labelbilemezsin. Bu etiketin basit varlığı, basit inceleme ile kod hata ayıklama yeteneğinizi yok eder . Bir veya iki dallı küçük fonksiyonlar için problem değil. Küçük olmayan işlevler için ...
90'lı yılların başlarında, bize 3 boyutlu bir grafik ekran kullanan ve daha hızlı çalışmasını sağlayan bir C kodu verildi. Bu kod sadece 5000 hakkında hatları, ama hepsi o oldu mainve yazar 15 hakkında kullanılan ya da öylesine gotoher iki yönde dallanma s. Başlamak için bu kötü bir koddu, ama bunların varlığı gotoonu daha da kötüleştirdi. İş arkadaşımın kontrol akışını bulması yaklaşık 2 hafta sürdü. Daha da iyisi, gotobunlar kodla sonuçlandı ki kendisiyle o kadar sıkı bir şekilde birleşti ki , hiçbir şeyi bozmadan herhangi bir değişiklik yapamadık .
Seviye 1 optimizasyonu ile derlemeye çalıştık ve derleyici mevcut tüm RAM'leri, ardından mevcut olan tüm takasları yedi ve sonra sistemi panikledi (muhtemelen s'lerin gotokendileriyle hiçbir ilgisi yoktu , ama o anekdotu oraya atmayı seviyorum).
Sonunda, müşteriye iki seçenek sunduk - her şeyi sıfırdan yeniden yazalım ya da daha hızlı donanım alalım.
Daha hızlı donanım aldılar.
Bode'nin kullanım kuralları goto:
- Sadece ileriye doğru şube;
- Kontrol yapılarını atlamayın (yani bir
ifveya forveya whileifadesinin gövdesine dalmayın);
gotoKontrol yapısı yerine kullanmayın
Orada bir durumlardır goto olan doğru cevabı, ancak nadirdir (bir derin iç içe döngünün dışında kırarak sadece ben kullanıyorum ediyorum koyun hakkındadır).
DÜZENLE
Bu son ifadeye göre, işte geçerli olan birkaç geçerli kullanım durumundan biri goto. Aşağıdaki işleve sahip olduğumuzu varsayalım:
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
for ( i = 0; i < N; i ++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
for ( j = 0; j < M; j++ )
{
arr[i][j] = malloc( sizeof *arr[i][j] * P );
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
return arr;
}
Şimdi, bir sorunumuz var - peki ya mallocaramalardan biri yarıda başarısız olursa? Olası bir olay olması muhtemel olarak, kısmen tahsis edilmiş bir dizi döndürmek istemiyoruz, fonksiyondan bir hatayla kurtulmak da istemiyoruz; Kendimizden sonra temizlik yapmak ve herhangi bir kısmen tahsis edilmiş hafızayı boşaltmak istiyoruz. Kötü bir tahsisat için istisna yapan bir dilde, bu oldukça basittir - zaten tahsis edilmiş olanı serbest bırakmak için bir istisna işleyicisi yazmanız yeterlidir.
C'de, yapılandırılmış istisna işlemeniz yoktur; Her mallocaramanın geri dönüş değerini kontrol etmeniz ve uygun işlemi yapmanız gerekir .
T ***myalloc( size_t N, size_t M, size_t P )
{
size_t i, j, k;
T ***arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( i = 0; i < N; i ++ )
{
if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
goto cleanup_1;
for ( j = 0; j < M; j++ )
{
if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
goto cleanup_2;
for ( k = 0; k < P; k++ )
arr[i][j][k] = initial_value();
}
}
}
goto done;
cleanup_2:
// We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
while ( j-- )
free( arr[i][j] );
free( arr[i] );
// fall through
cleanup_1:
// We failed while allocating arr[i]; free up all previously allocated arr[i][j]
while ( i-- )
{
for ( j = 0; j < M; j++ )
free( arr[i][j] );
free( arr[i] );
}
free( arr );
arr = NULL;
done:
return arr;
}
Bunu kullanmadan yapabilir miyiz goto? Tabii ki yapabiliriz - bu sadece biraz ekstra defter tutma gerektirir (ve pratikte benim alacağım yol budur). Ancak, kullanmanın hemen kötü bir uygulama veya tasarım belirtisi gotoolmadığı yerler arıyorsanız , bu az sayıdakilerden biridir.