GOTO masih dianggap berbahaya? [Tutup]

282

Semua orang mengetahui Surat Dijkstra kepada editor: buka pernyataan yang dianggap berbahaya (juga di sini. Transkrip html dan di sini. Pdf) dan telah ada dorongan yang kuat sejak saat itu untuk menghindari pernyataan goto kapan pun memungkinkan. Meskipun dimungkinkan untuk menggunakan goto untuk menghasilkan kode yang luas dan tidak dapat dipelihara, namun tetap dalam bahasa pemrograman modern . Bahkan struktur kontrol lanjutan lanjutan dalam Skema dapat digambarkan sebagai goto canggih.

Keadaan apa yang menjamin penggunaan goto? Kapan sebaiknya menghindari?

Sebagai pertanyaan tindak lanjut: C menyediakan sepasang fungsi, setjmp dan longjmp, yang menyediakan kemampuan untuk pergi tidak hanya dalam frame stack saat ini tetapi dalam salah satu frame panggilan. Haruskah ini dianggap berbahaya seperti goto? Lebih berbahaya?


Dijkstra sendiri menyesalkan gelar itu, yang tidak bertanggung jawab padanya. Di akhir EWD1308 (juga di sini .pdf) ia menulis:

Akhirnya sebuah cerita pendek sebagai catatan. Pada tahun 1968, Komunikasi ACM menerbitkan teks tambang dengan judul " Pernyataan goto dianggap berbahaya ", yang pada tahun-tahun berikutnya akan paling sering dirujuk, namun sayangnya, sering oleh penulis yang tidak melihat lebih dari itu daripada yang judul, yang menjadi landasan ketenaran saya dengan menjadi templat: kita akan melihat semua jenis artikel dengan judul "X dianggap berbahaya" untuk hampir semua X, termasuk yang berjudul "Dijkstra dianggap berbahaya". Tetapi apa yang terjadi? Saya telah menyerahkan sebuah makalah dengan judul " Kasus yang menentang pernyataan kebohongan", yang, untuk mempercepat penerbitannya, editor telah berubah menjadi" surat kepada Editor ", dan dalam prosesnya ia memberinya judul baru dari penemuannya sendiri! Editornya adalah Niklaus Wirth.

Makalah klasik yang dipikirkan dengan baik tentang topik ini, untuk dicocokkan dengan Dijkstra, adalah Pemrograman Terstruktur dengan pergi ke Pernyataan , oleh Donald E. Knuth. Membaca keduanya membantu membangun kembali konteks dan pemahaman non-dogmatis tentang subjek. Dalam tulisan ini, pendapat Dijkstra tentang kasus ini dilaporkan dan bahkan lebih kuat:

Donald E. Knuth: Saya percaya bahwa dengan menyajikan pandangan seperti itu saya sebenarnya tidak setuju dengan ide-ide Dijkstra, karena dia baru-baru ini menulis yang berikut: "Tolong jangan jatuh ke dalam perangkap percaya bahwa saya sangat dogmatis tentang [the lanjut ke pernyataan]. Saya memiliki perasaan tidak nyaman bahwa orang lain membuat agama dari itu, seolah-olah masalah konseptual pemrograman dapat diselesaikan dengan satu trik, dengan bentuk disiplin kode yang sederhana! "

MaD70
sumber
1
Goto C # tidak sama dengan goto yang Dijkstra bicarakan, karena alasan yang tepat yang Dijkstra bicarakan. Itu goto adalah baik berbahaya seperti itu kembali kemudian, tetapi juga banyak kurang perlu, karena bahasa modern menyediakan struktur kontrol alternatif. C # goto sangat dibatasi.
Jalf
25
Foto bagus ketika mereka menambah kejelasan. Jika Anda memiliki loop bersarang panjang, bisa lebih baik untuk keluar dari itu daripada mengatur variabel "break" dan mematahkan sampai Anda keluar.
simendsjo
28
Jika Anda memiliki loop bersarang di 4 kedalaman (bukan berarti itu hal yang baik), keluar dari semua memerlukan pengaturan nilai sementara. Sebuah goto di sini jauh lebih jelas bagi saya, dan IDE harus dengan mudah menunjukkan di mana goto itu berada. Yang mengatakan, penggunaan goto harus jarang, dan menurut saya hanya bergerak turun untuk melewati kode
simendsjo
7
Saya sarankan Anda membaca sembilan ribu satu utas yang ditandai goto.
Matti Virkkunen
5
Ada satu hal yang jelas lebih buruk daripada menggunakan goto: meretas alat pemrograman terstruktur bersama untuk mengimplementasikan a goto.

Jawaban:

184

Pernyataan berikut adalah generalisasi; Meskipun selalu memungkinkan untuk memohon pengecualian, biasanya (menurut pengalaman dan pendapat saya yang rendah hati) tidak sebanding dengan risikonya.

  1. Penggunaan alamat memori yang tidak dibatasi (baik GOTO atau raw pointer) memberikan terlalu banyak peluang untuk membuat kesalahan yang mudah dihindari.
  2. Semakin banyak cara untuk sampai pada "lokasi" tertentu dalam kode, semakin kurang percaya diri tentang keadaan sistem pada saat itu. (Lihat di bawah.)
  3. IMHO pemrograman terstruktur kurang tentang "menghindari GOTO" dan lebih lanjut tentang membuat struktur kode sesuai dengan struktur data. Misalnya, struktur data berulang (misalnya array, file sekuensial, dll.) Secara alami diproses oleh unit kode yang diulang. Memiliki struktur bawaan (misalnya, sementara, untuk, sampai, untuk masing-masing, dll.) Memungkinkan programmer untuk menghindari kebosanan mengulangi pola kode klise yang sama.
  4. Sekalipun GOTO adalah detail implementasi level rendah (tidak selalu demikian!) Itu di bawah level yang seharusnya dipikirkan oleh programmer. Berapa banyak programmer yang menyeimbangkan buku cek pribadi mereka dalam biner mentah? Berapa banyak programmer yang khawatir tentang sektor mana pada disk yang berisi catatan tertentu, alih-alih hanya menyediakan kunci untuk mesin basis data (dan berapa banyak cara yang dapat terjadi kesalahan jika kami benar-benar menulis semua program kami dalam hal sektor disk fisik)?

Catatan kaki di atas:

Mengenai poin 2, pertimbangkan kode berikut:

a = b + 1
/* do something with a */

Pada titik "lakukan sesuatu" dalam kode, kita dapat menyatakan dengan keyakinan tinggi yang alebih besar dari b. (Ya, saya mengabaikan kemungkinan bilangan bulat bilangan bulat yang tidak terbungkus. Mari kita tidak mengurangi contoh sederhana.)

Di sisi lain, jika kode sudah dibaca dengan cara ini:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

Banyaknya cara untuk mendapatkan label 10 berarti bahwa kita harus bekerja lebih keras untuk menjadi percaya diri tentang hubungan antara adan bpada saat itu. (Faktanya, dalam kasus umum itu tidak dapat dipastikan!)

Mengenai poin 4, seluruh gagasan "pergi ke suatu tempat" dalam kode hanyalah metafora. Tidak ada yang benar-benar "masuk" di mana saja di dalam CPU kecuali elektron dan foton (untuk panas yang terbuang). Terkadang kita memberikan metafora untuk yang lain, lebih bermanfaat, satu. Saya ingat menemukan (beberapa dekade yang lalu!) Bahasa di mana

if (some condition) {
  action-1
} else {
  action-2
}

diimplementasikan pada mesin virtual dengan mengkompilasi action-1 dan action-2 sebagai rutinitas tanpa parameter out-of-line, kemudian menggunakan opcode VM dua argumen tunggal yang menggunakan nilai boolean dari kondisi untuk memanggil satu atau yang lain. Konsepnya hanyalah "memilih apa yang akan dipanggil sekarang" daripada "pergi ke sini atau pergi ke sana". Sekali lagi, hanya perubahan metafora.

joel.neely
sumber
2
Poin yang bagus. Dalam bahasa tingkat yang lebih tinggi, goto bahkan tidak berarti apa-apa (pertimbangkan untuk beralih antar metode di Jawa). Fungsi Haskell dapat terdiri dari satu ekspresi; coba lompat keluar dengan goto!
Siput mekanik
2
Catatan tambahan berfungsi seperti contoh poin 4 Anda.
luser droog
Smalltalk bekerja mirip dengan contoh poin 4, jika dengan "sama" Anda berarti "tidak seperti bahasa prosedural lakukan". : P Tidak ada kontrol aliran dalam bahasa; semua keputusan ditangani melalui polimorfisme ( truedan falsedari jenis yang berbeda), dan setiap cabang dari if / else pada dasarnya adalah lambda.
cao
Ini adalah poin yang valid, tetapi pada akhirnya mereka hanya mengulangi betapa buruknya kebohongan "jika disalahgunakan". Break, lanjutkan, keluar, kembali, gosub, settimeout, global, termasuk, dll. Adalah semua teknik modern yang membutuhkan mental melacak aliran hal-hal dan semua dapat disalahgunakan untuk membuat kode spaghetti dan melompat-lompat untuk menciptakan ketidakpastian keadaan variabel. Agar adil, meskipun saya belum pernah melihat penggunaan goto yang buruk secara langsung, saya juga hanya pernah melihatnya digunakan sekali atau dua kali. Itu berbicara pada pernyataan bahwa selalu ada hal-hal yang lebih baik untuk digunakan.
Beejor
gotodalam bahasa pemrograman modern (Go) stackoverflow.com/a/11065563/3309046 .
nishanths
245

Komik GOTO XKCD

Seorang rekan kerja saya mengatakan satu-satunya alasan untuk menggunakan GOTO adalah jika Anda memprogram diri sendiri sejauh ini ke sudut yang merupakan satu-satunya jalan keluar. Dengan kata lain, desain yang tepat sebelumnya dan Anda tidak perlu menggunakan GOTO nanti.

Saya pikir komik ini menggambarkan bahwa dengan indah, "Saya bisa merestrukturisasi aliran program, atau menggunakan sedikit 'GOTO' sebagai gantinya." GOTO adalah jalan keluar yang lemah ketika Anda memiliki desain yang lemah. Velociraptors memangsa yang lemah .

Jim McKeeth
sumber
41
GOTO dapat membuat 'lompat' dari satu tempat sewenang-wenang ke tempat sewenang-wenang lainnya. Velociraptor melompat ke sini entah dari mana!
rpattabi
29
saya tidak menganggap lelucon itu lucu sama sekali karena semua orang tahu bahwa Anda juga harus menautkannya sebelum dapat mengeksekusi.
Kinjal Dixit
31
Rekan kerja Anda salah, dan jelas tidak membaca koran Knuth.
Jim Balter
27
Saya telah melihat ada kode yang membingungkan dan sebaliknya berubah selama bertahun-tahun hanya untuk menghindari kebohongan. @ jimmcKeeth, Kartun xkcd di atas tidak membuktikan bahwa goto lemah. Ini mengolok-olok histeria saat menggunakannya.
4
Anda menyadari bahwa kode sumber perpustakaan Delphi berisi pernyataan goto. Dan ini adalah penggunaan goto yang tepat.
David Heffernan
131

Terkadang valid untuk menggunakan GOTO sebagai alternatif untuk penanganan pengecualian dalam satu fungsi:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

Kode COM tampaknya masuk ke dalam pola ini cukup sering.

Rob Walker
sumber
29
Saya setuju, ada kasus penggunaan yang sah di mana, goto dapat menyederhanakan kode dan membuatnya lebih mudah dibaca / dikelola, tetapi tampaknya ada semacam goto-fobia yang berkeliaran ...
Pop Catalin
48
@ Bob: Sulit untuk memindahkan kode err_cleanup ke subrutin jika itu membersihkan variabel lokal.
Niki
4
Sebenarnya, saya menggunakannya di COM / VB6 hanya karena saya tidak punya alternatif, bukan karena itu adalah alternatif. Betapa bahagianya saya saat ini dengan mencoba / menangkap / akhirnya.
Rui Craveiro
10
@ user4891 Cara C ++ idiomatis tidak mencoba {} catch () {cleanup; }, melainkan, RAII, di mana sumber daya yang perlu dibersihkan dilakukan dalam destruktor. Setiap konstruktor / destruktor mengelola tepat satu sumber daya.
David Stone
5
Ada dua cara penulisan ini dalam C tanpa goto; dan keduanya jauh lebih pendek. Baik: if (f ()) if (g ()) if (h ()) mengembalikan kesuksesan; membersihkan(); kegagalan pengembalian; atau: jika (f () && g () && h ()) mengembalikan kesuksesan; membersihkan(); kegagalan pengembalian;
LHP
124

Saya hanya ingat menggunakan goto sekali. Saya memiliki serangkaian lima loop terhitung bersarang dan saya harus dapat keluar dari seluruh struktur dari dalam awal berdasarkan kondisi tertentu:

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

Saya bisa saja dengan mudah mendeklarasikan variabel break boolean dan menggunakannya sebagai bagian dari kondisional untuk setiap loop, tetapi dalam hal ini saya memutuskan GOTO sama praktis dan mudah dibaca.

Tidak ada velociraptors yang menyerang saya.

shsteimer
sumber
83
"Refactor menjadi fungsi dan ganti goto dengan return :)", dan perbedaannya adalah? sungguh apa bedanya? tidak bisa kembali juga? Pengembalian juga mengerem aliran terstruktur seperti goto, dan dalam hal ini mereka melakukannya dengan cara yang sama (bahkan jika goto dapat digunakan untuk hal-hal yang lebih kejam)
Pop Catalin
38
Bersarang banyak loop biasanya merupakan bau kode sendiri. Kecuali jika Anda melakukan, seperti, perkalian array 5 dimensi, sulit untuk menggambarkan situasi di mana beberapa loop batin tidak dapat dengan berguna diekstraksi menjadi fungsi yang lebih kecil. Seperti semua aturan praktis, saya kira ada beberapa pengecualian.
Doug McClean
5
Menggantinya dengan pengembalian hanya berfungsi jika Anda menggunakan bahasa yang mendukung pengembalian.
Loren Pechtel
31
@ leppie: Generasi yang memberontak gotodan memberi kami pemrograman terstruktur juga menolak pengembalian awal, karena alasan yang sama. Turun ke seberapa mudah dibaca kode, seberapa jelas itu menyatakan maksud programmer. Membuat fungsi tanpa tujuan lain selain menghindari penggunaan kata kunci yang difitnah mengakibatkan kohesi yang buruk: penyembuhannya lebih buruk daripada penyakitnya.
Mud
21
@ButtleButkus: Terus terang, itu sama buruknya, kalau tidak lebih buruk. Setidaknya dengan a goto, seseorang dapat secara eksplisit menentukan target. Dengan break 5;, (1) saya harus menghitung penutupan loop untuk menemukan tujuan; dan (2) jika struktur loop pernah berubah, mungkin perlu mengubah nomor itu agar tujuan tetap benar. Jika saya akan menghindari goto, maka keuntungannya adalah tidak harus melacak hal-hal seperti itu secara manual.
cHao
93

Kami sudah melakukan diskusi ini dan saya mendukung pendapat saya .

Selain itu, saya muak dengan orang-orang yang menggambarkan struktur bahasa tingkat tinggi sebagai “goto menyamar” karena mereka jelas tidak mengerti maksudnya sama sekali . Sebagai contoh:

Bahkan struktur kontrol lanjutan lanjutan dalam Skema dapat digambarkan sebagai goto canggih.

Itu omong kosong. Setiap struktur kontrol dapat diimplementasikan dalam hal gototetapi pengamatan ini benar-benar sepele dan tidak berguna. gototidak dianggap berbahaya karena efek positifnya tetapi karena konsekuensi negatifnya dan ini telah dieliminasi oleh pemrograman terstruktur.

Demikian pula, mengatakan "GOTO adalah alat, dan karena semua alat, itu dapat digunakan dan disalahgunakan" benar-benar melenceng. Tidak ada pekerja bangunan modern yang akan menggunakan batu dan mengklaimnya "adalah alat." Batuan telah digantikan oleh palu. gototelah digantikan oleh struktur kontrol. Jika pekerja konstruksi terdampar di alam liar tanpa palu, tentu saja dia akan menggunakan batu. Jika seorang programmer harus menggunakan bahasa pemrograman inferior yang tidak memiliki fitur X, well, tentu saja ia mungkin harus menggunakannya goto. Tetapi jika dia menggunakannya di tempat lain alih-alih fitur bahasa yang sesuai, dia jelas tidak mengerti bahasa dengan benar dan menggunakannya dengan salah. Ini sangat sederhana.

Konrad Rudolph
sumber
82
Tentu saja, penggunaan batu yang tepat bukanlah sebagai palu. Salah satu kegunaannya yang tepat adalah batu gerinda, atau untuk mengasah alat lainnya. Bahkan batu yang rendah, bila digunakan dengan benar adalah alat yang bagus. Anda hanya perlu menemukan penggunaan yang tepat. Sama berlaku untuk goto.
Kibbee
10
Jadi apa gunanya penggunaan Goto? Untuk setiap kasus yang bisa dibayangkan ada alat lain yang lebih cocok. Dan bahkan batu gerinda Anda sebenarnya digantikan oleh alat berteknologi tinggi saat ini, bahkan jika itu masih terbuat dari batu. Ada perbedaan besar antara bahan baku dan alat.
Konrad Rudolph
8
@jalf: Goto pasti tidak ada pada C #. Lihat stackoverflow.com/questions/359436/...
Jon Skeet
51
Saya kecewa begitu banyak orang menyetujui posting ini. Posting Anda hanya tampak efektif karena Anda tidak pernah repot-repot mempertanyakan logika apa yang sebenarnya Anda lakukan, sehingga Anda gagal memperhatikan kesalahan Anda. Izinkan saya untuk memparafrasekan seluruh posting Anda: "Ada alat yang unggul untuk goto dalam setiap situasi, jadi goto tidak boleh digunakan." Ini adalah bikondisi logis, dan dengan demikian seluruh posting Anda pada dasarnya mengemis pertanyaan "Bagaimana Anda tahu ada alat yang unggul untuk goto dalam setiap situasi?"
Pengodean Dengan Gaya
10
@ Kode: Tidak, Anda benar-benar merindukan inti dari posting. Itu adalah balasan daripada argumen yang terisolasi dan lengkap. Saya hanya menunjukkan kekeliruan dalam argumen utama "untuk" goto. Anda benar sejauh saya tidak menawarkan argumen terhadap diri gotosendiri - saya tidak bermaksud - jadi tidak ada pertanyaan-mengemis.
Konrad Rudolph
91

Goto sangat rendah dalam daftar hal untuk dimasukkan dalam program hanya demi itu. Itu tidak berarti itu tidak dapat diterima.

Goto bisa bagus untuk mesin negara. Pernyataan switch dalam satu loop adalah (dalam urutan kepentingan khas): (a) sebenarnya tidak mewakili aliran kontrol, (b) jelek, (c) berpotensi tidak efisien tergantung pada bahasa dan kompiler. Jadi Anda akhirnya menulis satu fungsi per negara, dan melakukan hal-hal seperti "kembalikan NEXT_STATE;" yang bahkan terlihat seperti goto.

Memang, sulit untuk kode mesin negara dengan cara yang membuatnya mudah dimengerti. Namun, tidak ada kesulitan yang berkaitan dengan penggunaan goto, dan tidak ada yang dapat dikurangi dengan menggunakan struktur kontrol alternatif. Kecuali jika bahasa Anda memiliki konstruksi 'mesin negara'. Milik saya tidak.

Pada saat-saat langka ketika algoritma Anda benar-benar paling mudah dipahami dalam hal jalur melalui urutan node (keadaan) yang dihubungkan oleh serangkaian transisi yang diizinkan (gotos), daripada oleh aliran kontrol yang lebih spesifik (loop, conditional, lainnya) ), maka itu harus eksplisit dalam kode. Dan Anda harus menggambar diagram yang cantik.

setjmp / longjmp bisa bagus untuk menerapkan pengecualian atau perilaku seperti pengecualian. Meskipun tidak dipuji secara universal, pengecualian umumnya dianggap sebagai struktur kontrol "valid".

setjmp / longjmp lebih 'berbahaya' daripada goto dalam arti bahwa mereka lebih sulit untuk digunakan dengan benar, apalagi pikiran komprehensif.

Tidak pernah ada, juga tidak akan pernah ada, bahasa apa pun di mana sedikit sulit untuk menulis kode yang buruk. - Donald Knuth.

Mengambil goto dari C tidak akan membuatnya lebih mudah untuk menulis kode yang baik dalam C. Bahkan, lebih baik melewatkan titik bahwa C seharusnya mampu bertindak sebagai bahasa assembler yang dimuliakan.

Selanjutnya akan menjadi "pointer dianggap berbahaya", kemudian "mengetik bebek dianggap berbahaya". Lalu siapa yang akan ditinggalkan untuk membela Anda ketika mereka datang untuk mengambil konstruksi pemrograman Anda yang tidak aman? Eh?

Steve Jessop
sumber
14
Personaly, ini adalah komentar yang akan saya berikan cek. Satu hal yang ingin saya tunjukkan kepada para pembaca adalah bahwa istilah "mesin negara" esoteris mencakup hal-hal sehari-hari seperti analisis leksikal. Lihat output lex suatu saat. Penuh dengan foto.
TED
2
Anda dapat menggunakan statement switch di dalam loop (atau event handler) untuk melakukan state machine dengan baik. Saya sudah melakukan banyak mesin negara tanpa harus menggunakan jmp atau goto.
Scott Whitlock
11
+1 Panah-panah pada mesin negara memetakan ke 'kebagian' lebih dekat daripada ke struktur kontrol lainnya. Tentu, Anda dapat menggunakan sakelar di dalam lingkaran - sama seperti Anda dapat menggunakan banyak goto alih-alih sementara untuk masalah lain, tetapi biasanya itu adalah ide; yang merupakan inti dari diskusi ini.
Edmund
2
Bisakah saya mengutip Anda pada paragraf terakhir itu?
Chris Lutz
2
Dan, di sini pada tahun 2013, kami telah mencapai (dan agak melewati) fase "pointer dianggap berbahaya".
Isiah Meadows
68

Di Linux: Menggunakan goto Dalam Kode Kernel pada Kernel Trap, ada diskusi dengan Linus Torvalds dan "orang baru" tentang penggunaan GOTO dalam kode Linux. Ada beberapa poin yang sangat bagus di sana dan Linus mengenakan kesombongan seperti itu :)

Beberapa bagian:

Linus: "Tidak, Anda telah dicuci otak oleh orang-orang CS yang berpikir bahwa Niklaus Wirth benar-benar tahu apa yang ia bicarakan. Dia tidak tahu. Dia tidak memiliki petunjuk yang menarik."

-

Linus: "Saya pikir goto baik-baik saja, dan sering lebih mudah dibaca daripada lekukan dalam jumlah besar."

-

Linus: "Tentu saja, dalam bahasa bodoh seperti Pascal, di mana label tidak bisa deskriptif, goto's bisa buruk."

Marcio Aguiar
sumber
9
Itu poin bagus bagaimana? Mereka mendiskusikan penggunaannya dalam bahasa yang tidak memiliki hal lain. Saat Anda memprogram dalam perakitan, semua cabang dan lompatan adalah milik goto. Dan C adalah, dan dulu, "bahasa rakitan portabel". Selain itu, bagian-bagian yang Anda kutip tidak mengatakan apa - apa tentang mengapa menurutnya kebotakan itu baik.
jalf
5
Wow. Itu mengecewakan untuk dibaca. Anda akan berpikir seorang pria OS besar seperti Linus Torvalds akan tahu lebih baik daripada mengatakan itu. Pascal (pascal sekolah lama, bukan versi Object modern) adalah apa yang ditulis Mac OS Classic selama periode 68k, dan itu adalah sistem operasi paling canggih pada masanya.
Mason Wheeler
6
@mason Classic Mac OS memiliki beberapa pustaka Pascal (akhirnya - runtime Pascal menghabiskan terlalu banyak memori di Mac awal) tetapi sebagian besar kode inti ditulis dalam Assembler, terutama grafik dan rutinitas UI.
Jim Dovey
30
Linus hanya berargumen (secara eksplisit, seperti Rik van Riel dalam diskusi itu) untuk goto untuk menangani status keluar, dan ia melakukannya berdasarkan kompleksitas yang akan dibawa oleh konstruksi alternatif C jika digunakan sebagai gantinya.
Charles Stewart
21
IMHO Linus benar tentang masalah ini. Maksudnya adalah bahwa kode kernel, ditulis dalam C, yang perlu mengimplementasikan sesuatu yang mirip dengan penanganan pengecualian, paling jelas dan sederhana ditulis dengan menggunakan goto. Idiom goto cleanup_and_exitadalah salah satu dari beberapa "baik" penggunaan goto tersisa sekarang bahwa kita memiliki for, while, dan ifuntuk mengelola aliran kendali kita. Lihat juga: programmers.stackexchange.com/a/154980
steveha
50

Dalam C, goto hanya berfungsi dalam lingkup fungsi saat ini, yang cenderung melokalisasi bug potensial. setjmpdan longjmpjauh lebih berbahaya, bersifat non-lokal, rumit dan tergantung pada implementasi. Namun dalam praktiknya, mereka terlalu kabur dan jarang menyebabkan banyak masalah.

Saya percaya bahwa bahaya goto di C sangat dibesar-besarkan. Ingatlah bahwa gotoargumen aslinya terjadi pada zaman bahasa seperti BASIC kuno, di mana pemula akan menulis kode spageti seperti ini:

3420 IF A > 2 THEN GOTO 1430

Di sini Linus menjelaskan penggunaan yang tepat dari goto: http://www.kernel.org/doc/Documentation/CodingStyle (bab 7).

smh
sumber
7
Ketika BASIC pertama kali tersedia, tidak ada alternatif untuk GOTO nnnn dan GOSUB mmmm sebagai cara untuk melompat-lompat. Konstruksi terstruktur ditambahkan kemudian.
Jonathan Leffler
7
Anda tidak mengerti intinya ... bahkan saat itu Anda tidak perlu menulis spageti ... GOTO Anda selalu dapat digunakan dengan cara yang disiplin
JoelFan
Perlu juga dicatat bahwa perilaku setjmp/ longjmphanya ditentukan ketika mereka digunakan sebagai cara melompat ke tempat dalam lingkup dari tempat lain dalam lingkup yang sama. Setelah kontrol meninggalkan ruang lingkup di mana setjmpdieksekusi, setiap upaya untuk digunakan longjmppada struktur yang dibuat oleh setjmpakan menghasilkan perilaku yang tidak ditentukan.
supercat
9
Beberapa versi BASIC memungkinkan Anda melakukannya GOTO A * 40 + B * 200 + 30. Tidak sulit untuk melihat bagaimana ini sangat berguna, dan sangat berbahaya.
Jon Hanna
1
@ Mengajaknya akan menghitung ekspresi dan kemudian pergi ke baris kode dengan nomor itu (nomor garis eksplisit adalah persyaratan dari sebagian besar dialek sebelumnya). ZX Spectrum Basic adalah salah satu yang akan menerima itu
Jon Hanna
48

Hari ini, sulit untuk melihat masalah besar tentang GOTOpernyataan itu karena "pemrograman terstruktur" kebanyakan orang memenangkan perdebatan dan bahasa saat ini memiliki struktur aliran kontrol yang cukup untuk dihindari GOTO.

Hitung jumlah gotos dalam program C modern. Sekarang tambahkan jumlah break, continuedan returnpernyataan. Selanjutnya, tambahkan berapa kali Anda menggunakan if, else, while,switch atau case. Itu tentang berapa banyakGOTO program Anda akan memiliki jika Anda menulis dalam FORTRAN atau BASIC pada tahun 1968 ketika Dijkstra menulis suratnya.

Bahasa pemrograman pada saat itu kurang dalam aliran kontrol. Misalnya, dalam BASIC Dartmouth asli:

  • IFpernyataan tidak ELSE. Jika Anda menginginkannya, Anda harus menulis:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • Bahkan jika IFpernyataan Anda tidak memerlukan ELSE, itu masih terbatas pada satu baris, yang biasanya terdiri dari a GOTO.

  • Tidak ada DO...LOOPpernyataan. Untuk non- FORloop, Anda harus mengakhiri loop dengan eksplisit GOTOatau IF...GOTOkembali ke awal.

  • Tidak ada SELECT CASE. Anda harus menggunakanON...GOTO .

Jadi, Anda berakhir dengan banyak dari GOTOdalam program Anda. Dan Anda tidak dapat bergantung pada pembatasan GOTOs ke dalam satu subrutin tunggal (karena GOSUB...RETURNitu adalah konsep subrutin yang lemah), sehingga ini GOTObisa mana saja . Jelas, ini membuat aliran kontrol sulit untuk diikuti.

Dari sinilah GOTOdatangnya gerakan anti gerakan.

dan04
sumber
Satu hal yang perlu diperhatikan adalah bahwa cara penulisan kode yang disukai jika seseorang memiliki beberapa kode dalam satu loop yang harus jarang dieksekusi adalah 420 if (rare_condition) then 3000// 430 and onward: rest of loop and other main-line code// 3000 [code for rare condition]// 3230 goto 430. Menulis kode dengan cara seperti itu menghindari cabang yang diambil atau melompat dalam kasus garis utama umum, tetapi hal itu membuat hal-hal sulit untuk diikuti. Penghindaran cabang dalam kode rakitan bisa lebih buruk, jika beberapa cabang terbatas pada mis. +/- 128 byte dan kadang-kadang tidak memiliki pasangan pelengkap (mis. "Cjne" ada tetapi tidak "cje").
supercat
Saya pernah menulis beberapa kode untuk 8x51 yang memiliki interupsi yang berjalan setiap 128 siklus. Setiap siklus tambahan yang dihabiskan dalam kasus umum ISR akan mengurangi kecepatan eksekusi kode jalur utama lebih dari 1% (saya pikir sekitar 90 siklus dari 128 biasanya tersedia untuk jalur utama), dan setiap instruksi percabangan akan memakan waktu dua siklus ( dalam kasus percabangan dan fall-through). Kode memiliki dua perbandingan - yang biasanya akan melaporkan sama; yang lain, tidak sama. Dalam kedua kasus, kode kasus langka lebih dari 128 byte. Jadi ...
supercat
cjne r0,expected_value,first_comp_springboard/.../ cjne r1,unexpected_value,second_comp_fallthrough// `ajmp second_comp_target` // first_comp_springboard: ajmp first_comp_target// second_comp_fallthrough: .... Bukan pola pengkodean yang sangat bagus, tetapi ketika siklus individu dihitung, seseorang melakukan hal-hal seperti itu. Tentu saja, pada tahun 1960-an, tingkat optimasi seperti itu lebih penting daripada hari ini, terutama karena CPU modern sering memerlukan optimasi aneh, dan sistem kompilasi just-in-time mungkin dapat menerapkan kode optimisasi untuk CPU yang tidak ada ketika kode yang dimaksud ditulis.
supercat
34

Go To dapat menyediakan semacam stand-in untuk penanganan pengecualian "nyata" dalam kasus tertentu. Mempertimbangkan:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

Jelas kode ini disederhanakan untuk mengambil lebih sedikit ruang, jadi jangan terlalu terpaku pada detail. Tetapi pertimbangkan alternatif yang saya lihat terlalu banyak dalam kode produksi oleh coders yang tidak masuk akal untuk menghindari penggunaan goto:

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

Sekarang secara fungsional kode ini melakukan hal yang sama persis. Bahkan, kode yang dihasilkan oleh kompiler hampir identik. Namun, dalam semangat programmer untuk menenangkan Nogoto (dewa teguran akademis yang ditakuti), programmer ini telah benar-benar melanggar idiom mendasar yang whilediwakili oleh loop, dan melakukan bilangan real pada keterbacaan kode.Ini tidak lebih baik.

Jadi, moral dari cerita ini adalah, jika Anda menemukan diri Anda menggunakan sesuatu yang benar-benar bodoh untuk menghindari penggunaan goto, maka jangan lakukan itu.

tylerl
sumber
1
Meskipun saya cenderung setuju dengan apa yang Anda katakan di sini, fakta bahwa breakpernyataan itu berada di dalam struktur kontrol membuat jelas apa yang mereka lakukan. Dengan gotocontoh, orang yang membaca kode harus melihat kode untuk menemukan label, yang secara teori, sebenarnya bisa sebelum struktur kontrol. Saya tidak cukup berpengalaman dengan gaya lama C untuk membuat penilaian bahwa yang satu pasti lebih baik daripada yang lain, tetapi ada trade-off yang baik.
Sungai Vivian
5
@DanielAllenLangdon: Fakta bahwa breaks berada di dalam sebuah loop membuat jelas bahwa mereka keluar dari loop . Itu bukan "apa yang mereka lakukan", karena pada kenyataannya, tidak ada loop sama sekali! Tidak ada di dalamnya yang pernah memiliki kesempatan untuk mengulang, tetapi itu tidak jelas sampai akhir. Fakta bahwa Anda memiliki "lingkaran" yang tidak pernah berjalan lebih dari sekali, berarti bahwa struktur kontrol disalahgunakan. Dengan gotocontoh itu, programmer bisa mengatakan goto error_handler;. Ini lebih eksplisit, dan bahkan lebih sulit untuk diikuti. (Ctrl + F, "error_handler:" untuk menemukan target. Coba lakukan itu dengan "}".)
cHao
1
Saya pernah melihat kode yang mirip dengan contoh kedua Anda dalam sistem kontrol lalu lintas udara - karena 'goto tidak ada dalam leksikon kami'.
QED
30

Donald E. Knuth menjawab pertanyaan ini dalam buku "Programate Literate", 1992 CSLI. Pada p. 17 ada esai " Pemrograman Terstruktur dengan Pernyataan goto " (PDF). Saya pikir artikel itu mungkin telah diterbitkan di buku-buku lain juga.

Artikel tersebut menggambarkan saran Dijkstra dan menjelaskan keadaan di mana ini berlaku. Tetapi dia juga memberikan sejumlah contoh counter (masalah dan algoritma) yang tidak dapat dengan mudah direproduksi menggunakan loop terstruktur saja.

Artikel ini berisi deskripsi lengkap tentang masalah, sejarah, contoh dan contoh balasan.

Bruno Ranschaert
sumber
25

Goto dianggap membantu.

Saya mulai pemrograman pada tahun 1975. Bagi pemrogram era 1970-an, kata "goto dianggap berbahaya" kurang lebih mengatakan bahwa bahasa pemrograman baru dengan struktur kontrol modern layak untuk dicoba. Kami memang mencoba bahasa baru. Kami dengan cepat bertobat. Kami tidak pernah kembali.

Kami tidak pernah kembali, tetapi, jika Anda lebih muda, maka Anda tidak pernah ada di tempat pertama.

Sekarang, latar belakang dalam bahasa pemrograman kuno mungkin tidak terlalu berguna kecuali sebagai indikator usia programmer. Meskipun demikian, programmer yang lebih muda tidak memiliki latar belakang ini, sehingga mereka tidak lagi memahami pesan yang disampaikan slogan "goto dianggap berbahaya" kepada audiens yang dituju pada saat diperkenalkan.

Slogan-slogan yang tidak dimengerti tidak terlalu mencerahkan. Mungkin lebih baik melupakan slogan-slogan seperti itu. Slogan seperti itu tidak membantu.

Slogan khusus ini, "Goto dianggap berbahaya," telah mengambil nyawa mayat hidup sendiri.

Bisakah goto tidak disalahgunakan? Jawab: tentu, tapi terus kenapa? Praktis setiap elemen pemrograman dapat disalahgunakan. Yang rendah hatibool misalnya disalahgunakan lebih sering daripada yang ingin dipercaya oleh sebagian dari kita.

Sebaliknya, saya tidak ingat pernah bertemu satu pun contoh penyalahgunaan goto sejak tahun 1990.

Masalah terbesar dengan goto mungkin bukan teknis tetapi sosial. Programmer yang tidak tahu banyak kadang-kadang tampaknya merasa bahwa kebosanan membuat mereka terdengar pintar. Anda mungkin harus memuaskan programmer seperti itu dari waktu ke waktu. Begitulah hidup.

Hal terburuk tentang goto hari ini adalah tidak cukup digunakan.

thb
sumber
24

Tertarik oleh Jay Ballou menambahkan jawaban, saya akan menambahkan £ 0,02 saya. Jika Bruno Ranschaert belum melakukannya, saya akan menyebutkan artikel "Pemrograman Terstruktur dengan Pernyataan GOTO" Knuth.

Satu hal yang belum pernah saya lihat dibahas adalah jenis kode yang, meskipun tidak terlalu umum, diajarkan dalam buku teks Fortran. Hal-hal seperti rentang diperpanjang dari loop DO dan subrutin kode terbuka (ingat, ini akan menjadi Fortran II, atau Fortran IV, atau Fortran 66 - bukan Fortran 77 atau 90). Setidaknya ada kemungkinan detail sintaksisnya tidak eksak, tetapi konsepnya harus cukup akurat. Cuplikan di setiap kotak berada di dalam satu fungsi.

Perhatikan bahwa buku yang sangat bagus tetapi sudah usang (dan sudah tidak dicetak) ' The Elements of Programming Style, 2nd Edn ' oleh Kernighan & Plauger menyertakan beberapa contoh nyata penyalahgunaan GOTO dari buku-buku teks pemrograman pada masanya (akhir 70-an). Namun, materi di bawah ini bukan dari buku itu.

Kisaran yang diperluas untuk loop DO

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

Salah satu alasan untuk omong kosong seperti itu adalah kartu punch kuno yang bagus. Anda mungkin memperhatikan bahwa label (baik di luar urutan karena itu gaya kanonik!) Ada di kolom 1 (sebenarnya, mereka harus di kolom 1-5) dan kode ada di kolom 7-72 ​​(kolom 6 adalah kelanjutan kolom penanda). Kolom 73-80 akan diberi nomor urut, dan ada mesin yang akan mengurutkan kartu punch menjadi urutan nomor urut. Jika Anda memiliki program Anda pada kartu berurutan dan perlu menambahkan beberapa kartu (garis) ke tengah-tengah loop, Anda harus mengulang semuanya setelah garis-garis ekstra. Namun, jika Anda mengganti satu kartu dengan barang GOTO, Anda dapat menghindari penyetelan kembali semua kartu - Anda hanya menyelipkan kartu baru di akhir rutin dengan nomor urut baru. Anggap itu sebagai upaya pertama 'komputasi hijau'

Oh, Anda mungkin juga mencatat bahwa saya curang dan tidak berteriak - Fortran IV ditulis dalam huruf besar semua secara normal.

Subrutin kode terbuka

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

GOTO antara label 76 dan 54 adalah versi goto yang dihitung. Jika variabel i memiliki nilai 1, kebagian label pertama dalam daftar (123); jika memiliki nilai 2, kebagian yang kedua, dan seterusnya. Fragmen dari 76 ke goto dihitung adalah subrutin kode terbuka. Itu adalah sepotong kode yang dieksekusi agak seperti subrutin, tetapi ditulis dalam tubuh fungsi. (Fortran juga memiliki fungsi pernyataan - yang merupakan fungsi tertanam yang dipasang pada satu baris.)

Ada konstruksi yang lebih buruk daripada goto yang dihitung - Anda bisa menetapkan label ke variabel dan kemudian menggunakan goto yang ditugaskan. Googling ditugaskan goto kepada memberi tahu saya itu dihapus dari Fortran 95. Kapur satu demi revolusi pemrograman terstruktur yang dapat dikatakan telah dimulai di depan umum dengan surat atau artikel "GOTO Considered Harmful" karya Dijkstra.

Tanpa pengetahuan tentang hal-hal yang dilakukan di Fortran (dan dalam bahasa lain, yang sebagian besar benar-benar jatuh di pinggir jalan), sulit bagi kita para pendatang baru untuk memahami ruang lingkup masalah yang ditangani oleh Dijkstra. Heck, saya tidak memulai pemrograman sampai sepuluh tahun setelah surat itu diterbitkan (tapi saya mengalami ketidakberuntungan untuk program di Fortran IV untuk sementara waktu).

Jonathan Leffler
sumber
2
Jika Anda ingin melihat contoh beberapa kode menggunakan goto'in the wild', pertanyaan Dicari: Bekerja Algoritma Sortir Bose-Hibbard menunjukkan beberapa kode (Algol 60) yang diterbitkan pada tahun 1963. Tata letak aslinya tidak dibandingkan dengan standar pengkodean modern. Kode yang diklarifikasi (indentasi) masih cukup sulit dipahami. The gotopernyataan ada lakukan membuatnya (sangat) sulit untuk memahami apa algoritma terserah.
Jonathan Leffler
1
Karena terlalu muda untuk mengalami apa pun yang dekat dengan kartu punch, itu mencerahkan untuk membaca tentang masalah meninju kembali. +1
Qix - MONICA DISALAHKAN
20

Tidak ada hal-hal seperti GOTO yang dianggap berbahaya .

GOTO adalah alat, dan karena semua alat, itu dapat digunakan dan disalahgunakan .

Namun, ada banyak alat di dunia pemrograman yang cenderung disalahgunakan lebih daripada digunakan , dan GOTO adalah salah satunya. yang DENGAN pernyataan Delphi adalah hal lain.

Secara pribadi saya tidak menggunakan salah satu dari kode yang umum , tetapi saya sudah memiliki penggunaan aneh GOTO dan WITH yang dibenarkan, dan solusi alternatif akan berisi lebih banyak kode.

Solusi terbaik adalah bagi kompiler untuk hanya memperingatkan Anda bahwa kata kunci itu ternoda , dan Anda harus memasukkan beberapa arahan pragma di sekitar pernyataan untuk menghilangkan peringatan.

Ini seperti memberi tahu anak-anak Anda untuk tidak lari dengan gunting . Gunting memang tidak buruk, tetapi beberapa penggunaannya mungkin bukan cara terbaik untuk menjaga kesehatan Anda.

Lasse V. Karlsen
sumber
Masalah dengan GOTO adalah bahwa ia merusak invarian penting yang kami terima begitu saja dengan bahasa pemrograman modern. Untuk mengambil satu contoh, jika saya memanggil suatu fungsi, kita mengasumsikan bahwa ketika fungsi selesai, itu akan mengembalikan kontrol ke penelepon, baik secara normal, atau melalui tumpukan luar biasa. Jika fungsi itu salah menggunakan GOTO, maka tentu saja ini tidak lagi benar. Ini membuatnya sangat sulit untuk berpikir tentang kode kita. Tidaklah cukup untuk menghindari penyalahgunaan GOTO dengan hati-hati. Masalahnya masih terjadi jika GOTO disalahgunakan oleh dependensi kita ...
Jonathan Hartley
... Jadi untuk mengetahui apakah kami dapat memberi alasan tentang pemanggilan fungsi kami, kami perlu memeriksa setiap baris kode sumber dari setiap dependensi transitif kami, memastikan bahwa mereka tidak menyalahgunakan GOTO. Jadi, keberadaan GOTO dalam bahasa tersebut telah merusak kemampuan kami untuk secara meyakinkan memberi alasan tentang kode kami sendiri, bahkan jika kami menggunakannya dengan sempurna (atau tidak pernah menggunakannya) sendiri. Untuk alasan ini, GOTO bukan hanya alat untuk digunakan dengan hati-hati. Secara sistemik rusak dan keberadaannya dalam bahasa secara sepihak dianggap berbahaya.
Jonathan Hartley
Meskipun gotokata kunci dihapus dari C #, konsep "lompat di sini" masih ada di IL. Infinite loop dapat dengan mudah dibangun tanpa kata kunci goto. Jika tidak ada jaminan bahwa kode yang dipanggil akan kembali, menurut Anda, berarti "tidak dapat alasan tentang kode", maka saya akan mengatakan kami tidak pernah memiliki kemampuan itu.
Lasse V. Karlsen
Ah, Anda telah menemukan sumber dari miskomunikasi kami. The gotodalam C # bukan "goto" nyata dalam arti asli dari kata tersebut. Ini adalah versi yang jauh lebih lemah yang hanya memungkinkan untuk melompat dalam suatu fungsi. Perasaan di mana saya menggunakan "goto" memungkinkan untuk melompat di mana saja dalam proses. Jadi, meskipun C # memiliki kata kunci "goto", kata itu bisa saja tidak pernah memiliki, goto nyata. Ya, goto nyata tersedia di tingkat IL, dengan cara yang sama ketika bahasa apa pun dikompilasi ke perakitan. Tetapi programmer terlindung dari itu, tidak dapat menggunakannya dalam keadaan normal, sehingga tidak masuk hitungan.
Jonathan Hartley
Kebetulan, pendapat yang saya uraikan di sini pada awalnya bukan milik saya, tetapi merupakan inti dari makalah asli Dijkstra dari tahun 1967, saya pikir, ditulis ulang oleh editornya "Goto dianggap berbahaya", yang telah menjadi meme yang sering dikutip untuk 50 orang. bertahun-tahun justru karena sangat revolusioner, berwawasan luas, dan diterima secara universal.
Jonathan Hartley
17

Tidak pernah ada, selama Anda mampu berpikir untuk diri sendiri.

Stesch
sumber
17

Sejak saya mulai melakukan beberapa hal di kernel linux, goto tidak terlalu mengganggu saya seperti dulu. Pada awalnya saya agak ngeri melihat mereka (kernel guys) menambahkan gotos ke dalam kode saya. Sejak itu saya menjadi terbiasa dengan penggunaan goto, dalam beberapa konteks terbatas, dan sekarang kadang-kadang akan menggunakannya sendiri. Biasanya, ini adalah kebalikan yang melompat ke akhir fungsi untuk melakukan semacam pembersihan dan bail out, daripada menduplikasi pembersihan dan bailout yang sama di beberapa tempat dalam fungsi. Dan biasanya, itu bukan sesuatu yang cukup besar untuk diserahkan ke fungsi lain - misalnya membebaskan beberapa variabel malloc'ed lokal adalah kasus khas.

Saya sudah menulis kode yang hanya menggunakan setjmp / longjmp. Itu dalam program sequencer drum MIDI. Putar ulang terjadi dalam proses terpisah dari semua interaksi pengguna, dan proses pemutaran menggunakan memori bersama dengan proses UI untuk mendapatkan info terbatas yang diperlukan untuk melakukan pemutaran. Ketika pengguna ingin menghentikan pemutaran, proses pemutaran hanya melakukan longjmp "kembali ke awal" untuk memulai dari awal, alih-alih beberapa pelonggaran rumit di mana pun itu akan dieksekusi ketika pengguna ingin berhenti. Itu bekerja dengan baik, sederhana, dan saya tidak pernah memiliki masalah atau bug yang terkait dengannya dalam hal itu.

setjmp / longjmp memiliki tempat mereka - tetapi tempat itu adalah salah satu yang Anda tidak akan mungkin mengunjungi tetapi sekali dalam waktu yang sangat lama.

Sunting: Saya baru saja melihat kode. Sebenarnya siglongjmp () yang saya gunakan, bukan longjmp (bukan itu masalah besar, tapi saya lupa bahwa siglongjmp bahkan ada.)

smcameron
sumber
15

Jika Anda menulis VM di C, ternyata menggunakan goto yang dikomputasi (gcc) seperti ini:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

bekerja jauh lebih cepat daripada saklar konvensional di dalam sebuah loop.

vt.
sumber
Satu-satunya masalah, itu bukan standar, kan?
Calmarius
&&op_inctentu saja tidak mengkompilasi, karena (kiri) &mengharapkan nilai, tetapi (kanan) &menghasilkan nilai.
fredoverflow
7
@ FredO: Ini adalah operator GCC khusus. Namun, saya akan menolak kode ini dalam semua kecuali keadaan terburuk, karena saya pasti tidak dapat memahami apa yang sedang terjadi.
Anak anjing
Meskipun ini adalah salah satu contoh (maks. Pengoptimalan untuk kecepatan) yang membenarkan penggunaan kode goto dan kriptik, masih harus banyak dikomentari untuk menjelaskan perlunya optimasi, kira-kira cara kerjanya, dan garis-demi-garis terbaik. komentar yang bisa dibuat. Jadi, itu masih gagal, tetapi karena itu meninggalkan programmer pemeliharaan dalam gelap. Tapi saya suka contoh VM. Terima kasih.
MicroservicesOnDDD
14

Karena gotodapat digunakan untuk membingungkan metaprogramming

Gotoadalah ekspresi kontrol level tinggi dan level rendah , dan sebagai hasilnya ia tidak memiliki pola desain yang sesuai yang cocok untuk sebagian besar masalah.

Ini tingkat rendah dalam arti bahwa goto adalah operasi primitif yang mengimplementasikan sesuatu yang lebih tinggi seperti whileatauforeach atau sesuatu.

Ini tingkat tinggi dalam arti bahwa ketika digunakan dengan cara tertentu dibutuhkan kode yang mengeksekusi dalam urutan yang jelas, dengan cara yang tidak terputus, kecuali untuk loop terstruktur, dan mengubahnya menjadi potongan-potongan logika yang, dengan cukup gotos, ambil -tas logika sedang disusun kembali secara dinamis.

Jadi, ada membosankan dan jahat sisi ke goto.

Sisi prosa adalah bahwa goto menunjuk ke atas dapat menerapkan loop yang sangat masuk akal dan goto menunjuk ke bawah dapat melakukan hal yang masuk akal breakatau return. Tentu saja, yang aktual while,, breakatau returnakan jauh lebih mudah dibaca, karena manusia miskin tidak perlu mensimulasikan efek gotountuk mendapatkan gambaran besar. Jadi, ide yang buruk secara umum.

Sisi jahat melibatkan rutinitas tidak menggunakan goto untuk sementara, istirahat, atau kembali, tetapi menggunakannya untuk apa yang disebut logika spageti . Dalam hal ini pengembang goto-senang membangun potongan kode dari labirin goto, dan satu-satunya cara untuk memahaminya adalah dengan mensimulasikannya secara keseluruhan, tugas yang sangat melelahkan ketika ada banyak goto. Maksud saya, bayangkan kesulitan mengevaluasi kode di mana elsetidak persis kebalikan dari itu if, di mana bersarang ifmungkin memungkinkan dalam beberapa hal yang ditolak oleh luar if, dll, dll.

Akhirnya, untuk benar-benar membahas subjek, kita harus mencatat bahwa pada dasarnya semua bahasa awal kecuali Algol awalnya hanya membuat pernyataan tunggal yang tunduk pada versi mereka if-then-else. Jadi, satu-satunya cara untuk melakukan blok bersyarat adalah gotomengelilinginya menggunakan kondisional terbalik. Gila, saya tahu, tapi saya sudah membaca beberapa spesifikasi lama. Ingat bahwa komputer pertama diprogram dalam kode mesin biner jadi saya kira segala jenis HLL adalah penyelamat; Saya kira mereka tidak terlalu pilih-pilih tentang fitur HLL apa yang mereka dapatkan.

Setelah mengatakan semua yang saya gunakan untuk memasukkan satu gotoke setiap program saya menulis "hanya untuk mengganggu para puritan" .

DigitalRoss
sumber
4
+1 untuk mengganggu para puritan! :-)
Lumi
10

Menolak penggunaan pernyataan GOTO kepada programmer seperti memberi tahu tukang kayu untuk tidak menggunakan palu karena dapat merusak dinding saat dia memalu paku. Seorang programmer sejati Mengetahui Bagaimana dan Kapan menggunakan GOTO. Saya telah mengikuti di belakang beberapa yang disebut 'Program Terstruktur' ini. Saya telah melihat kode Horrid hanya untuk menghindari penggunaan GOTO, sehingga saya bisa menembak programmer. Ok, untuk membela pihak lain, saya telah melihat beberapa kode spaghetti sungguhan juga, para programmer itu juga harus ditembak.

Ini hanya satu contoh kecil dari kode yang saya temukan.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------ATAU----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10
CurtTampa
sumber
3
DO CRT 'Apakah ini benar? (Y / T): ': INPUT YORN SAMPAI YORN =' Y 'ATAU YORN =' N '; dll
joel.neely
5
Memang, tetapi yang lebih penting, seorang programmer sejati tahu kapan tidak menggunakan goto- dan tahu mengapa . Menghindari konstruksi bahasa yang tabu karena $ programming_guru mengatakan demikian, itulah definisi pemrograman pemujaan kargo.
Piskvor meninggalkan gedung
1
Ini analogi yang bagus. Untuk menggerakkan kuku tanpa merusak substrat, Anda tidak menghilangkan palu. Sebaliknya, Anda menggunakan alat sederhana yang dikenal sebagai paku paku. Ini adalah pin logam dengan ujung meruncing yang memiliki lekukan berongga di ujungnya, untuk dipasangkan secara aman dengan kepala kuku (alat ini tersedia dalam berbagai ukuran untuk kuku yang berbeda). Ujung kuku yang tumpul lainnya dipukul dengan palu.
Kaz
9

"Di tautan ini http://kerneltrap.org/node/553/2131 "

Ironisnya, menghilangkan goto memperkenalkan bug: panggilan spinlock dihilangkan.

Jay Ballou
sumber
+1 untuk 'menghilangkan goto memperkenalkan bug'
QED
7

Makalah asli harus dianggap sebagai "GOTO Tanpa Syarat Dianggap Berbahaya". Itu khususnya menganjurkan bentuk pemrograman berdasarkan pada conditional ( if) dan iterative ( while) constructs, daripada test-and-jump yang umum pada kode awal. gotomasih berguna dalam beberapa bahasa atau keadaan, di mana tidak ada struktur kontrol yang sesuai.

John Millikin
sumber
6

Tentang satu-satunya tempat yang saya setujui Goto dapat digunakan adalah ketika Anda perlu menangani kesalahan, dan setiap titik tertentu kesalahan terjadi memerlukan penanganan khusus.

Misalnya, jika Anda mengambil sumber daya dan menggunakan semaphore atau mutex, Anda harus mengambilnya secara berurutan dan Anda harus selalu melepaskannya dengan cara yang berlawanan.

Beberapa kode memerlukan pola yang sangat aneh untuk mengambil sumber daya ini, dan Anda tidak bisa hanya menulis struktur kontrol yang mudah dipelihara dan dipahami untuk menangani dengan baik pengambilan dan pelepasan sumber daya ini untuk menghindari jalan buntu.

Selalu mungkin untuk melakukannya dengan benar tanpa kebohongan, tetapi dalam kasus ini dan beberapa lainnya, kebalikannya sebenarnya adalah solusi yang lebih baik terutama untuk keterbacaan dan pemeliharaan.

-Adam

Adam Davis
sumber
5

Salah satu penggunaan GOTO modern adalah oleh kompiler C # untuk membuat mesin negara untuk enumerator yang ditentukan oleh pengembalian hasil.

GOTO adalah sesuatu yang harus digunakan oleh kompiler dan bukan programmer.

Brian Leahy
sumber
5
Menurut Anda siapa sebenarnya yang membuat kompiler?
tloach
10
Kompiler, tentu saja!
Seiti
3
Saya pikir maksudnya "GOTO adalah sesuatu yang seharusnya hanya digunakan oleh kode yang dipancarkan oleh kompiler".
Simon Buchan
Ini adalah kasus yang menentang goto. Di mana kita dapat menggunakan gotomesin negara yang dikodekan dengan tangan untuk mengimplementasikan enumerator, kita bisa menggunakan yieldsaja.
Jon Hanna
aktifkan banyak string (untuk mencegah kompilasi ke jika-lain) dengan kompilasi case default untuk beralih dengan pernyataan goto.
M.kazem Akhgary
5

Sampai C dan C ++ (di antara pelaku lainnya) telah memberi label istirahat dan berlanjut, goto akan terus memiliki peran.

DrPizza
sumber
Jadi berlabel break atau continue akan berbeda dengan goto bagaimana?
Matthew Whited
3
Mereka tidak mengizinkan lompatan yang sepenuhnya sewenang-wenang dalam aliran kontrol.
DrPizza
5

Jika GOTO itu sendiri jahat, kompiler akan menjadi jahat, karena mereka menghasilkan JMP. Jika melompat ke blok kode, terutama mengikuti pointer, secara inheren jahat, instruksi RETurn akan menjadi jahat. Sebaliknya, kejahatan berpotensi untuk disalahgunakan.

Kadang-kadang saya harus menulis aplikasi yang harus melacak sejumlah objek di mana setiap objek harus mengikuti urutan rumit negara dalam menanggapi peristiwa, tetapi semuanya pasti single-thread. Urutan khas negara, jika direpresentasikan dalam pseudo-code adalah:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

Saya yakin ini bukan hal baru, tetapi cara saya menanganinya dalam C (++) adalah mendefinisikan beberapa makro:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Kemudian (dengan asumsi awalnya 0) mesin state terstruktur di atas berubah menjadi kode terstruktur:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

Dengan variasi pada ini, mungkin ada CALL dan RETURN, sehingga beberapa mesin negara dapat bertindak seperti subrutin mesin negara lainnya.

Apakah ini tidak biasa? Iya. Apakah perlu belajar dari pihak pengelola? Iya. Apakah pembelajaran itu membuahkan hasil? Aku pikir begitu. Bisakah itu dilakukan tanpa GOTO yang melompat ke blok? Nggak.

Mike Dunlavey
sumber
1
Menghindari fitur bahasa karena takut adalah tanda kerusakan otak. Ini juga lebih baik dari neraka.
Vector Gorgoth
4

Saya menghindarinya karena rekan kerja / manajer pasti akan mempertanyakan penggunaannya baik dalam ulasan kode atau ketika mereka sengaja menemukannya. Meskipun saya pikir itu menggunakan (misalnya kasus penanganan kesalahan) - Anda akan bertabrakan dengan beberapa pengembang lain yang akan memiliki beberapa jenis masalah dengan itu.

Itu tidak layak.

Aardvark
sumber
Hal yang menyenangkan tentang Coba ... Tangkap blok di C # adalah bahwa mereka menangani pembersihan tumpukan dan sumber daya lainnya yang dialokasikan (disebut unwinding the stack) sebagai gelembung pengecualian hingga pengendali pengecualian. Ini membuat Coba ... Tangkap jauh lebih baik daripada Goto, jadi jika Anda memiliki Coba ... Tangkap, gunakan.
Scott Whitlock
Saya punya solusi yang lebih baik: bungkus potongan kode yang ingin Anda munculkan di 'do {...} while (0);' lingkaran. Dengan begitu, Anda bisa melompat keluar dengan cara yang sama seperti goto tanpa overhead dari try / catch loop (saya tidak tahu tentang C #, tetapi dalam C ++ coba berbiaya rendah & tangkapan berbiaya tinggi, jadi sepertinya berlebihan untuk melempar pengecualian di mana lompatan sederhana sudah cukup).
Jim Dovey
10
Jim, masalah dengan itu adalah bahwa itu tidak lebih dari cara memutar yang bodoh untuk mendapatkan kebohongan.
Coding With Style
4

Saya benar-benar menemukan diri saya terpaksa menggunakan goto, karena saya benar-benar tidak bisa memikirkan cara yang lebih baik (lebih cepat) untuk menulis kode ini:

Saya memiliki objek yang kompleks, dan saya perlu melakukan beberapa operasi padanya. Jika objek berada dalam satu keadaan, maka saya bisa melakukan versi cepat dari operasi, kalau tidak saya harus melakukan versi lambat dari operasi. Masalahnya adalah bahwa dalam beberapa kasus, di tengah operasi yang lambat, adalah mungkin untuk menyadari bahwa ini bisa dilakukan dengan operasi cepat.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

Ini adalah bagian penting dari kode UI waktu nyata, jadi saya benar-benar berpikir bahwa GOTO dibenarkan di sini.

Hugo

Roket
sumber
Cara non-GOTO adalah dengan menggunakan fungsi fast_calculations, yang menimbulkan beberapa overhead. Mungkin tidak terlihat di sebagian besar keadaan, tetapi seperti yang Anda katakan ini sangat penting.
Kyle Cronin
1
Yah itu tidak mengejutkan. Semua kode yang memiliki pedoman kinerja yang benar-benar gila akan selalu mematahkan hampir semua praktik terbaik. Praktik terbaik adalah untuk keterjangkauan, dan pemeliharaan, bukan untuk memeras 10 milidetik lagi atau menghemat 5 byte lebih banyak ram.
Jonathon
1
@JonathonWisnoski, penggunaan yang sah dari goto juga menghilangkan jumlah kode spagetti yang gila dengan campur tangan variabel sarang tikus untuk melacak di mana kita akan pergi.
vonbrand
4

Hampir semua situasi di mana goto dapat digunakan, Anda dapat melakukan hal yang sama menggunakan konstruksi lain. Lagipula Goto digunakan oleh kompiler.

Saya pribadi tidak pernah menggunakannya secara eksplisit, tidak perlu.

Adam Houldsworth
sumber
4

Satu hal yang saya tidak melihat dari salah satu jawaban di sini adalah bahwa solusi 'goto' sering lebih efisien dari satu solusi pemrograman terstruktur sering disebutkan.

Pertimbangkan kasus banyak-bersarang-loop, di mana menggunakan 'goto' bukan sekelompok if(breakVariable) bagian jelas lebih efisien. Solusi "Letakkan loop Anda dalam suatu fungsi dan gunakan kembali" seringkali sama sekali tidak masuk akal. Jika loop menggunakan variabel lokal, Anda sekarang harus melewati semuanya melalui parameter fungsi, berpotensi menangani banyak sakit kepala tambahan yang muncul dari itu.

Sekarang perhatikan kasus pembersihan, yang sering saya gunakan sendiri, dan sangat umum sehingga diduga bertanggung jawab atas struktur coba {} catch {} tidak tersedia dalam banyak bahasa. Jumlah pemeriksaan dan variabel tambahan yang diperlukan untuk mencapai hal yang sama jauh lebih buruk daripada satu atau dua instruksi untuk melakukan lompatan, dan sekali lagi, solusi fungsi tambahan bukanlah solusi sama sekali. Anda tidak dapat memberi tahu saya bahwa itu lebih mudah dikelola atau lebih mudah dibaca.

Sekarang ruang kode, penggunaan tumpukan, dan waktu eksekusi mungkin tidak cukup berarti dalam banyak situasi bagi banyak programmer, tetapi ketika Anda berada di lingkungan yang tertanam dengan hanya 2KB ruang kode untuk bekerja, 50 byte instruksi tambahan untuk menghindari satu yang didefinisikan dengan jelas 'goto' hanya bisa ditertawakan, dan ini bukanlah situasi yang jarang seperti yang diyakini oleh banyak programmer tingkat tinggi.

Pernyataan bahwa 'goto berbahaya' sangat membantu dalam bergerak menuju pemrograman terstruktur, bahkan jika itu selalu merupakan generalisasi berlebihan. Pada titik ini, kita semua sudah cukup mendengarnya untuk berhati-hati menggunakannya (sebagaimana seharusnya). Ketika itu jelas alat yang tepat untuk pekerjaan itu, kita tidak perlu takut akan hal itu.

gkimsey
sumber
3

Anda dapat menggunakannya untuk memecah dari loop yang sangat bersarang, tetapi sebagian besar waktu kode Anda dapat di refactored menjadi lebih bersih tanpa loop yang terlalu dalam.

Brian R. Bondy
sumber