Konteks historis: Kita harus ingat bahwa Dijkstra menulis Goto Dianggap Berbahaya pada tahun 1968, ketika banyak programmer menggunakan goto
sebagai pengganti pemrograman terstruktur (if
, while
, for
, dll.).
Ini 44 tahun kemudian, dan jarang ditemukan penggunaan goto
ini di alam liar. Pemrograman terstruktur sudah lama menang.
Analisis kasus:
Kode contoh terlihat seperti ini:
SETUP...
again:
COMPUTE SOME VALUES...
if (cmpxchg64(ptr, old_val, val) != old_val)
goto again;
Versi terstrukturnya terlihat seperti ini:
SETUP...
do {
COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);
Saat saya melihat versi terstrukturnya, saya langsung berpikir, "ini sebuah lingkaran". Ketika saya melihat goto
versi, saya menganggapnya sebagai garis lurus dengan kasus "coba lagi" di bagian akhir.
goto
versi keduanya memiliki SETUP
dan COMPUTE SOME VALUES
pada kolom yang sama, yang menekankan bahwa sebagian besar waktu, aliran kontrol melewati keduanya. Versi terstruktur menempatkan SETUP
dan COMPUTE SOME VALUES
pada kolom yang berbeda, yang menekankan bahwa kontrol dapat melewatinya secara berbeda.
Pertanyaannya di sini adalah penekanan seperti apa yang ingin Anda masukkan ke dalam kode? Anda dapat membandingkannya dengan goto
untuk penanganan kesalahan:
Versi terstruktur:
if (do_something() != ERR) {
if (do_something2() != ERR) {
if (do_something3() != ERR) {
if (do_something4() != ERR) {
...
Versi Goto:
if (do_something() == ERR) // Straight line
goto error; // |
if (do_something2() == ERR) // |
goto error; // |
if (do_something3() == ERR) // |
goto error; // V
if (do_something4() == ERR) // emphasizes normal control flow
goto error;
Kode yang dihasilkan pada dasarnya sama, jadi kita dapat menganggapnya sebagai masalah tipografi, seperti lekukan.
Pertanyaan yang sangat bagus, dan menurut saya hanya penulis yang dapat memberikan jawaban pasti. Saya akan menambahkan sedikit spekulasi saya dengan mengatakan bahwa itu bisa dimulai dengan menggunakannya untuk penanganan kesalahan, seperti yang dijelaskan oleh @Izkata dan gerbang kemudian terbuka untuk menggunakannya untuk loop dasar juga.
Penggunaan penanganan kesalahan adalah yang sah dalam pemrograman sistem, menurut pendapat saya. Suatu fungsi secara bertahap mengalokasikan memori seiring perkembangannya, dan jika terjadi kesalahan, itu akan goto
label yang sesuai untuk membebaskan sumber daya dalam urutan terbalik dari titik tersebut.
Jadi, jika kesalahan terjadi setelah alokasi pertama, itu akan melompat ke label kesalahan terakhir, untuk membebaskan satu sumber daya saja. Demikian pula, jika kesalahan terjadi setelah alokasi terakhir, itu akan melompat ke label kesalahan pertama dan dijalankan dari sana, membebaskan semua sumber daya hingga akhir fungsi. Pola penanganan kesalahan seperti itu masih perlu digunakan dengan hati-hati, terutama saat memodifikasi kode, pengujian valgrind dan unit sangat dianjurkan. Tapi ini bisa dibilang lebih mudah dibaca dan dipelihara daripada pendekatan alternatif.
Satu aturan emas menggunakan goto
adalah untuk menghindari apa yang disebut kode spageti. Cobalah menggambar garis di antara setiap goto
pernyataan dan labelnya masing-masing. Jika Anda memiliki garis yang bersilangan, Anda telah melewati garis :). Seperti penggunaan goto
sangat sulit dibaca dan merupakan sumber umum bug yang sulit dilacak, karena dapat ditemukan dalam bahasa seperti BASIC yang mengandalkannya untuk kontrol alur.
Jika Anda melakukan hanya satu putaran sederhana, Anda tidak akan mendapatkan garis yang bersilangan, jadi itu masih dapat dibaca dan dipertahankan, sebagian besar menjadi masalah gaya. Yang mengatakan, karena mereka dapat dengan mudah dilakukan dengan kata kunci loop yang disediakan bahasa, seperti yang telah Anda tunjukkan dalam pertanyaan Anda, rekomendasi saya tetap untuk menghindari penggunaan goto
untuk loop, hanya karena for
, do/while
atau while
konstruksi lebih elegan dengan desain.
Dalam kasus contoh ini, saya curiga ini tentang retrofit dukungan SMP ke dalam kode yang awalnya ditulis dengan cara yang tidak aman untuk SMP. Menambahkan goto again;
path jauh lebih sederhana dan tidak terlalu invasif daripada merestrukturisasi fungsi.
Saya tidak bisa mengatakan saya sangat menyukai gaya ini, tetapi saya juga berpikir bahwa menghindari goto
adalah salah arah karena alasan ideologis. Satu kasus khusus goto
penggunaan (berbeda dari contoh ini) adalah tempat goto
hanya digunakan untuk bergerak maju dalam suatu fungsi, tidak pernah mundur. Kelas penggunaan ini tidak pernah menghasilkan konstruksi lingkaran yang muncul dari goto
, dan ini hampir selalu merupakan cara paling sederhana dan paling jelas untuk menerapkan perilaku yang diperlukan (yang biasanya membersihkan dan mengembalikan kesalahan).