1) Penggunaan goto yang paling umum yang saya ketahui adalah meniru penanganan pengecualian dalam bahasa yang tidak menawarkannya, yaitu dalam C. (Kode yang diberikan oleh Nuclear di atas hanya itu.) Lihatlah kode sumber Linux dan Anda akan melihat bazillion gotos digunakan seperti itu; ada sekitar 100.000 foto dalam kode Linux menurut survei cepat yang dilakukan pada tahun 2013: http://blog.regehr.org/archives/894 . Penggunaan goto bahkan disebutkan dalam panduan gaya pengkodean Linux: https://www.kernel.org/doc/Documentation/CodingStyle . Sama seperti pemrograman berorientasi objek yang ditiru menggunakan struct yang diisi dengan pointer fungsi, goto memiliki tempat dalam pemrograman C. Jadi siapa yang benar: Dijkstra atau Linus (dan semua coders kernel Linux)? Ini teori vs praktik pada dasarnya.
Namun ada gotcha yang biasa untuk tidak memiliki dukungan tingkat kompiler dan memeriksa untuk konstruksi / pola umum: lebih mudah untuk menggunakannya salah dan memperkenalkan bug tanpa pemeriksaan waktu kompilasi. Windows dan Visual C ++ tetapi dalam mode C menawarkan penanganan pengecualian melalui SEH / VEH karena alasan ini: pengecualian berguna bahkan di luar bahasa OOP, yaitu dalam bahasa prosedural. Tetapi kompiler tidak selalu dapat menyimpan bacon Anda, bahkan jika ia menawarkan dukungan sintaksis untuk pengecualian dalam bahasa. Pertimbangkan sebagai contoh dari kasus terakhir bug "goto gagal" Apple SSL yang terkenal, yang hanya menggandakan satu goto dengan konsekuensi yang merusak ( https://www.imperialviolet.org/2014/02/22/applebug.html ):
if (something())
goto fail;
goto fail; // copypasta bug
printf("Never reached\n");
fail:
// control jumps here
Anda dapat memiliki bug yang sama persis menggunakan pengecualian yang didukung compiler, misalnya di C ++:
struct Fail {};
try {
if (something())
throw Fail();
throw Fail(); // copypasta bug
printf("Never reached\n");
}
catch (Fail&) {
// control jumps here
}
Tetapi kedua varian bug dapat dihindari jika kompiler menganalisis dan memperingatkan Anda tentang kode yang tidak terjangkau. Misalnya kompilasi dengan Visual C ++ di tingkat peringatan / W4 menemukan bug dalam kedua kasus. Java misalnya melarang kode yang tidak dapat dijangkau (di mana ia dapat menemukannya!) Karena alasan yang cukup bagus: itu kemungkinan merupakan bug dalam kode rata-rata Joe. Selama goto construct tidak membolehkan target yang tidak mudah diketahui oleh kompiler, seperti gotos ke alamat yang dihitung (**), kompiler tidak akan kesulitan untuk menemukan kode yang tidak terjangkau di dalam fungsi dengan gotos daripada menggunakan Dijkstra -Kode yang disetujui.
(**) Catatan Kaki: Gotos ke nomor baris yang dihitung dimungkinkan dalam beberapa versi Basic, misalnya GOTO 10 * x di mana x adalah variabel. Agak membingungkan, dalam Fortran "goto dihitung" mengacu pada konstruk yang setara dengan pernyataan beralih dalam C. Standar C tidak mengizinkan goto dihitung dalam bahasa, tetapi hanya gotos ke label yang dinyatakan secara statik / sintaksis. Namun GNU C memiliki ekstensi untuk mendapatkan alamat label (operator unary, awalan &&) dan juga memungkinkan goto ke variabel tipe void *. Lihat https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html untuk informasi lebih lanjut tentang sub-topik yang tidak jelas ini. Sisa dari posting ini tidak peduli dengan fitur GNU C yang tidak jelas itu.
Foto C standar (yaitu tidak dihitung) biasanya tidak menjadi alasan mengapa kode yang tidak dapat dijangkau tidak dapat ditemukan pada waktu kompilasi. Alasan yang biasa adalah kode logika seperti berikut. Diberikan
int computation1() {
return 1;
}
int computation2() {
return computation1();
}
Sama sulitnya bagi kompilator untuk menemukan kode yang tidak terjangkau dalam salah satu dari 3 konstruksi berikut:
void tough1() {
if (computation1() != computation2())
printf("Unreachable\n");
}
void tough2() {
if (computation1() == computation2())
goto out;
printf("Unreachable\n");
out:;
}
struct Out{};
void tough3() {
try {
if (computation1() == computation2())
throw Out();
printf("Unreachable\n");
}
catch (Out&) {
}
}
(Maafkan gaya pengkodean yang terkait dengan brace saya, tetapi saya mencoba untuk menjaga agar contohnya seringkas mungkin.)
Visual C ++ / W4 (bahkan dengan / Ox) gagal menemukan kode yang tidak dapat dijangkau dalam semua ini, dan karena Anda mungkin tahu masalah menemukan kode yang tidak dapat dijangkau secara umum tidak dapat diputuskan. (Jika Anda tidak mempercayai saya tentang hal itu: https://www.cl.cam.ac.uk/teaching/2006/OptComp/slides/lecture02.pdf )
Sebagai masalah terkait, Coto bisa digunakan untuk meniru pengecualian hanya di dalam tubuh fungsi. Pustaka C standar menawarkan sepasang fungsi setjmp () dan longjmp () untuk meniru keluar / pengecualian non-lokal, tetapi mereka memiliki beberapa kelemahan serius dibandingkan dengan apa yang ditawarkan bahasa lain. Artikel Wikipedia http://en.wikipedia.org/wiki/Setjmp.h menjelaskan dengan cukup baik masalah terakhir ini. Pasangan fungsi ini juga berfungsi di Windows ( http://msdn.microsoft.com/en-us/library/yz2ez4as.aspx ), tetapi hampir tidak ada yang menggunakannya di sana karena SEH / VEH lebih unggul. Bahkan di Unix, saya pikir setjmp dan longjmp sangat jarang digunakan.
2) Saya pikir penggunaan kedua yang paling umum dari goto di C adalah mengimplementasikan multi-level break atau multi-level continue, yang juga merupakan kasus penggunaan yang tidak kontroversial. Ingatlah bahwa Java tidak mengizinkan label goto, tetapi mengizinkan label break atau melanjutkan label. Menurut http://www.oracle.com/technetwork/java/simple-142616.html , ini sebenarnya kasus penggunaan yang paling umum dari gotos di C (90% kata mereka), tetapi dalam pengalaman subjektif saya, kode sistem cenderung untuk menggunakan goto untuk penanganan kesalahan lebih sering. Mungkin dalam kode ilmiah atau di mana OS menawarkan penanganan pengecualian (Windows) maka keluar multi-level adalah kasus penggunaan yang dominan. Mereka tidak benar-benar memberikan perincian tentang konteks survei mereka.
Diedit untuk menambahkan: ternyata kedua pola penggunaan ini ditemukan dalam buku C Kernighan dan Ritchie, sekitar halaman 60 (tergantung pada edisi). Hal lain yang perlu diperhatikan adalah bahwa kedua use case hanya melibatkan forward gotos. Dan ternyata edisi MISRA C 2012 (tidak seperti edisi 2004) sekarang mengizinkan gotos, asalkan itu hanya yang maju.