Sebagai seorang programmer yang baik, seseorang harus menulis kode yang kuat yang akan menangani setiap hasil dari programnya. Namun, hampir semua fungsi dari pustaka C akan mengembalikan 0 atau -1 atau NULL ketika ada kesalahan.
Terkadang jelas bahwa pengecekan kesalahan diperlukan, misalnya ketika Anda mencoba membuka file. Tetapi saya sering mengabaikan kesalahan memeriksa fungsi seperti printf
atau bahkan malloc
karena saya merasa tidak perlu.
if(fprintf(stderr, "%s", errMsg) < 0){
perror("An error occurred while displaying the previous error.");
exit(1);
}
Apakah praktik yang baik untuk mengabaikan kesalahan tertentu, atau apakah ada cara yang lebih baik untuk menangani semua kesalahan?
c
error-handling
Derek 朕 會 功夫
sumber
sumber
try
pernyataan, sehingga Anda tidak perlu memeriksa setiap panggilan atau operasi. (Perhatikan juga bahwa beberapa bahasa lebih baik daripada yang lain dalam mendeteksi kesalahan sederhana seperti null dereference atau array index di luar batas.)errno
! Jika Anda tidak terbiasa, sementara itu benar bahwa "hampir semua fungsi dari pustaka C akan mengembalikan 0 atau −1 atauNULL
ketika ada kesalahan," mereka juga mengaturerrno
variabel global , yang dapat Anda akses dengan menggunakan#include <errno.h>
dan kemudian hanya membaca nilai darierrno
. Jadi, misalnya, jikaopen
(2) kembali-1
, Anda mungkin ingin memeriksa apakaherrno == EACCES
, yang akan menunjukkan kesalahan izin, atauENOENT
, yang akan menunjukkan bahwa file yang diminta tidak ada.try
/catch
, meskipun Anda bisa mensimulasikannya dengan lompatan.Jawaban:
Secara umum, kode harus menangani kondisi luar biasa di mana pun sesuai. Ya, ini adalah pernyataan yang tidak jelas.
Dalam bahasa tingkat yang lebih tinggi dengan penanganan pengecualian perangkat lunak, ini sering dinyatakan sebagai "tangkap pengecualian dalam metode di mana Anda benar-benar dapat melakukan sesuatu tentang hal itu." Jika terjadi kesalahan file, mungkin Anda membiarkannya menggelembungkan tumpukan ke kode UI yang benar-benar dapat memberi tahu pengguna "file Anda gagal disimpan ke disk." Mekanisme pengecualian secara efektif menelan "setiap kesalahan kecil" dan secara implisit menanganinya di tempat yang tepat.
Di C, Anda tidak memiliki kemewahan itu. Ada beberapa cara untuk menangani kesalahan, beberapa di antaranya adalah fitur bahasa / perpustakaan, beberapa di antaranya adalah praktik pengkodean.
Abaikan kesalahan tertentu? Mungkin. Sebagai contoh, masuk akal untuk berasumsi bahwa menulis ke output standar tidak akan gagal. Jika gagal, bagaimana Anda memberi tahu pengguna? Ya, itu ide yang baik untuk mengabaikan kesalahan tertentu, atau kode defensif untuk mencegahnya. Misalnya, periksa nol sebelum membaginya.
Ada beberapa cara untuk menangani semua, atau setidaknya sebagian besar kesalahan:
Cascading
if
s:Uji kondisi pertama, misalnya membuka atau membuat file, lalu menganggap operasi selanjutnya berhasil.
Kode yang kuat itu baik, dan orang harus memeriksa dan menangani kesalahan. Metode mana yang terbaik untuk kode Anda tergantung pada apa yang dilakukan oleh kode tersebut, seberapa kritis kegagalannya, dll. Dan hanya Anda yang dapat menjawabnya. Namun, metode ini telah teruji pertempuran dan digunakan dalam berbagai proyek sumber terbuka di mana Anda dapat melihat bagaimana kode nyata memeriksa kesalahan.
sumber
stdout
dapat diarahkan ke file, atau bahkan tidak ada tergantung pada sistem.stdout
bukan aliran "menulis ke konsol", biasanya seperti itu, tetapi tidak harus seperti itu.stdout
... jelas :-)dump_database > backups/db_dump.txt
gagal menulis ke output standar pada titik tertentu, saya tidak ingin itu melanjutkan dan keluar dengan sukses. (Bukan berarti database didukung seperti itu, tetapi intinya masih ada)Ya, tetapi Anda tahu fungsi mana yang Anda panggil, bukan?
Anda sebenarnya memiliki banyak informasi yang bisa Anda masukkan ke dalam pesan kesalahan. Anda tahu fungsi mana yang dipanggil, nama fungsi yang memanggilnya, parameter apa yang dilewati, dan nilai balik dari fungsi tersebut. Itu banyak informasi untuk pesan kesalahan yang sangat informatif.
Anda tidak harus melakukan ini untuk setiap panggilan fungsi. Tetapi saat pertama kali Anda melihat pesan kesalahan "Kesalahan terjadi saat menampilkan kesalahan sebelumnya," ketika apa yang benar-benar Anda butuhkan adalah informasi yang berguna, akan menjadi yang terakhir kali Anda melihat pesan kesalahan itu di sana, karena Anda akan segera mengubah pesan kesalahan ke sesuatu yang informatif yang akan membantu Anda memecahkan masalah.
sumber
if (!myfunc()) {/*proceed*/}
. 0 bukan kesalahan, dan apa pun yang bukan nol adalah kesalahan dan setiap kesalahan memiliki kode bukan nol sendiri. cara seorang teman saya mengatakannya "hanya ada satu Kebenaran, tetapi banyak kepalsuan." "0" harus "benar" dalamif()
tes dan apa pun yang bukan nol harus "salah".if(function()) { // do something }
dibaca sebagai "jika fungsi dijalankan tanpa kesalahan, maka lakukan sesuatu." atau, lebih baik lagi, "jika fungsi dijalankan dengan sukses, lakukan sesuatu." Serius, mereka yang memahami konvensi tidak bingung dengan ini. Mengembalikanfalse
(atau 0) ketika kesalahan terjadi tidak akan memungkinkan kode kesalahan digunakan. Nol berarti false dalam C karena itulah cara kerja matematika. Lihat programmers.stackexchange.com/q/198284TLDR; Anda seharusnya hampir tidak pernah mengabaikan kesalahan.
Bahasa C tidak memiliki fitur penanganan kesalahan yang baik sehingga setiap pengembang perpustakaan dapat mengimplementasikan solusi sendiri. Lebih banyak bahasa modern memiliki pengecualian di dalamnya yang membuat masalah khusus ini jauh lebih mudah untuk ditangani.
Tetapi ketika Anda terjebak dengan C Anda tidak memiliki fasilitas seperti itu. Sayangnya, Anda hanya perlu membayar harga setiap kali Anda memanggil fungsi yang kemungkinan kegagalannya kecil. Atau Anda akan menderita konsekuensi yang jauh lebih buruk seperti menimpa data dalam memori secara tidak sengaja. Jadi sebagai aturan umum Anda harus memeriksa kesalahan selalu.
Jika Anda tidak memeriksa kembalinya
fprintf
Anda kemungkinan besar meninggalkan bug yang dalam kasus terbaik tidak akan melakukan apa yang diharapkan pengguna dan kasus terburuk meledak semuanya selama penerbangan. Tidak ada alasan untuk melemahkan diri Anda seperti itu.Namun sebagai pengembang C itu juga tugas Anda untuk membuat kode mudah dipelihara. Jadi kadang-kadang Anda dapat dengan aman mengabaikan kesalahan demi kejelasan jika (dan hanya jika) mereka tidak menimbulkan ancaman terhadap perilaku keseluruhan aplikasi. .
Ini masalah yang sama dengan melakukan ini:
Jika Anda melihat bahwa tanpa komentar yang baik di dalam blok tangkap kosong ini tentu masalah. Tidak ada alasan untuk berpikir panggilan
malloc()
akan berhasil dilaksanakan 100% dari waktu.sumber
malloc()
adalah salah satu fungsi yang Anda pasti ingin memeriksa nilai kembali!Pertanyaannya sebenarnya bukan khusus bahasa, tetapi lebih khusus untuk pengguna. Pikirkan pertanyaan dari perspektif pengguna. Pengguna melakukan sesuatu, seperti mengetik nama program pada baris perintah dan menekan enter. Apa yang diharapkan pengguna? Bagaimana mereka tahu kalau ada yang salah? Apakah mereka dapat menengahi jika terjadi kesalahan?
Dalam banyak jenis kode, pemeriksaan semacam itu berlebihan. Namun, dalam kode kritis keselamatan yang keandalannya tinggi, seperti untuk reaktor nuklir, pengecekan kesalahan patologis dan jalur pemulihan yang direncanakan adalah bagian dari sifat pekerjaan sehari-hari. Itu dianggap sepadan dengan biaya untuk meluangkan waktu untuk bertanya, "Apa yang terjadi jika X gagal? Bagaimana saya kembali ke keadaan aman?" Dalam kode yang kurang andal, seperti yang untuk video game, Anda bisa lolos dengan pengecekan kesalahan yang jauh lebih sedikit.
Pendekatan lain yang serupa adalah seberapa banyak Anda benar-benar dapat memperbaiki keadaan dengan menangkap kesalahan? Saya tidak bisa menghitung jumlah program C ++ yang dengan bangga menangkap pengecualian, hanya untuk hanya memikirkan kembali mereka karena mereka tidak benar-benar tahu apa yang harus dilakukan dengan mereka ... tetapi mereka tahu mereka seharusnya melakukan penanganan pengecualian. Program-program semacam itu tidak memperoleh apa-apa dari upaya ekstra. Hanya tambahkan kode pemeriksaan kesalahan yang menurut Anda benar-benar dapat menangani situasi dengan lebih baik daripada tidak memeriksa kode kesalahan. Oleh karena itu, C ++ memiliki aturan khusus untuk menangani pengecualian yang terjadi selama penanganan pengecualian untuk menangkapnya dengan cara yang lebih bermakna (dan maksud saya, panggilan terminate () untuk memastikan tumpukan kayu yang Anda bangun untuk diri sendiri menyala dalam kemuliaan yang tepat)
Seberapa patologisnya Anda? Saya bekerja pada sebuah program yang mendefinisikan "SafetyBool" yang nilai benar dan salahnya di mana dipilih dengan cermat untuk memiliki pemerataan 1 dan 0, dan mereka dipilih sehingga salah satu dari sejumlah kegagalan perangkat keras (flip bit tunggal, bus data jejak rusak, dll.) tidak menyebabkan boolean disalahartikan. Tidak perlu dikatakan, saya tidak akan mengklaim ini sebagai praktik pemrograman tujuan umum untuk digunakan dalam program lama.
sumber
sumber
Sedikit abstrak mengambil pertanyaan. Dan itu belum tentu untuk bahasa C.
Untuk program yang lebih besar Anda akan memiliki lapisan abstraksi; mungkin bagian dari mesin, perpustakaan atau dalam kerangka kerja. Lapisan yang tidak akan peduli tentang cuaca Anda mendapatkan data yang valid atau output akan beberapa nilai default:
0
,-1
,null
dllLalu ada lapisan yang akan menjadi antarmuka Anda ke lapisan abstrak, yang akan melakukan banyak penanganan kesalahan dan mungkin hal-hal lain seperti suntikan ketergantungan, acara mendengarkan dll.
Dan nanti Anda akan memiliki layer implementasi konkret Anda di mana Anda benar-benar menetapkan aturan dan menangani hasilnya.
Jadi pendapat saya tentang hal ini adalah kadang-kadang lebih baik mengecualikan penanganan kesalahan dari bagian kode karena bagian itu tidak melakukan pekerjaan itu. Dan kemudian memiliki beberapa prosesor yang akan mengevaluasi output dan menunjukkan kesalahan.
Ini sebagian besar dilakukan untuk memisahkan tanggung jawab yang mengarah pada pembacaan kode, dan skalabilitas yang lebih baik.
sumber
Secara umum, kecuali Anda memiliki alasan kuat untuk tidak memeriksa kondisi kesalahan itu, Anda harus melakukannya. Satu-satunya alasan bagus yang dapat saya pikirkan untuk tidak memeriksa kondisi kesalahan adalah ketika Anda tidak dapat melakukan sesuatu yang berarti jika gagal. Ini adalah bar yang sangat sulit untuk dijumpai, karena selalu ada satu opsi yang layak: keluar dengan anggun.
sumber