Seperti yang orang lain katakan, masalahnya bukan pada goto
dirinya sendiri; masalahnya adalah bagaimana orang menggunakannya goto
, dan bagaimana kode itu membuat lebih sulit untuk dipahami dan dipelihara.
Asumsikan cuplikan kode berikut:
i = 4;
label: printf( "%d\n", i );
Untuk nilai apa dicetak i
? Kapan itu dicetak? Sampai Anda memperhitungkan setiap contoh goto label
dalam fungsi Anda, Anda tidak bisa tahu. Kehadiran sederhana label itu menghancurkan kemampuan Anda untuk men-debug kode dengan inspeksi sederhana. Untuk fungsi kecil dengan satu atau dua cabang, tidak banyak masalah. Untuk fungsi yang tidak kecil ...
Jauh di awal tahun 90-an kami diberi setumpuk kode C yang menggerakkan tampilan grafis 3d dan disuruh membuatnya berjalan lebih cepat. Itu hanya sekitar 5.000 baris kode, tetapi semuanya ada di main
, dan penulis menggunakan sekitar 15 atau lebih goto
bercabang di kedua arah. Ini adalah kode yang buruk untuk memulai, tetapi kehadiran mereka goto
membuatnya jauh lebih buruk. Butuh rekan kerja saya sekitar 2 minggu untuk memecahkan aliran kontrol. Lebih baik lagi, goto
itu menghasilkan kode yang begitu erat dengan dirinya sendiri sehingga kami tidak dapat membuat perubahan tanpa merusak sesuatu.
Kami mencoba mengkompilasi dengan optimasi level 1, dan kompiler memakan semua RAM yang tersedia, lalu semua swap yang tersedia, dan kemudian panik sistem (yang mungkin tidak ada hubungannya dengan goto
s sendiri, tapi saya suka membuang anekdot di luar sana).
Pada akhirnya, kami memberi pelanggan dua opsi - mari kita menulis ulang semuanya dari awal, atau membeli perangkat keras yang lebih cepat.
Mereka membeli perangkat keras yang lebih cepat.
Aturan Bode untuk menggunakan goto
:
- Cabang hanya maju;
- Jangan melewati struktur kontrol (yaitu, jangan bercabang ke dalam tubuh dari
if
atau for
atau while
pernyataan);
- Jangan gunakan
goto
menggantikan struktur kontrol
Ada kasus di mana a goto
adalah jawaban yang tepat, tetapi jarang terjadi (putus dari lingkaran yang bersarang adalah satu-satunya tempat saya menggunakannya).
EDIT
Memperluas pernyataan terakhir itu, inilah salah satu dari beberapa kasus penggunaan yang valid untuk goto
. Asumsikan kita memiliki fungsi berikut:
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;
}
Sekarang, kami memiliki masalah - bagaimana jika salah satu malloc
panggilan gagal di tengah jalan? Tidak seperti peristiwa yang mungkin terjadi, kami tidak ingin mengembalikan array yang dialokasikan sebagian, kami juga tidak ingin keluar dari fungsi dengan kesalahan; kami ingin membersihkan setelah diri kita sendiri dan menghapus semua memori yang dialokasikan sebagian. Dalam bahasa yang melempar pengecualian pada alokasi yang buruk, itu cukup mudah - Anda hanya menulis penangan pengecualian untuk membebaskan apa yang sudah dialokasikan.
Di C, Anda tidak memiliki penanganan pengecualian terstruktur; Anda harus memeriksa nilai balik dari setiap malloc
panggilan dan mengambil tindakan yang sesuai.
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;
}
Bisakah kita melakukan ini tanpa menggunakan goto
? Tentu saja kita bisa - itu hanya memerlukan sedikit pembukuan tambahan (dan, dalam praktiknya, itulah jalan yang akan saya ambil). Tetapi, jika Anda mencari tempat di mana menggunakan tanda goto
tidak langsung merupakan praktik atau desain yang buruk, ini adalah salah satu dari sedikit.