Diğerlerinin de söylediği gibi, problemin goto
kendisi değil ; Sorun insanların nasıl kullandığı goto
ve 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 label
bilemezsin. 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 main
ve yazar 15 hakkında kullanılan ya da öylesine goto
her iki yönde dallanma s. Başlamak için bu kötü bir koddu, ama bunların varlığı goto
onu daha da kötüleştirdi. İş arkadaşımın kontrol akışını bulması yaklaşık 2 hafta sürdü. Daha da iyisi, goto
bunlar 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 goto
kendileriyle 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
if
veya for
veya while
ifadesinin gövdesine dalmayın);
goto
Kontrol 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 malloc
aramalardan 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 malloc
aramanı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 goto
olmadığı yerler arıyorsanız , bu az sayıdakilerden biridir.