Dasar pemikiran di balik fungsi C library tidak pernah mengatur errno ke nol

9

Standar C mengamanatkan bahwa tidak ada fungsi perpustakaan standar C yang akan ditetapkan errnoke nol. Kenapa ini?

Saya bisa memahaminya berguna untuk memanggil beberapa fungsi, dan hanya memeriksa errnosetelah yang terakhir - misalnya:

errno = 0;
double x = strtod(str1, NULL);
long y = strtol(str2, NULL);
if (errno)
    // either "strtod" or "strtol" failed
else
    // both succeeded

Namun, apakah ini tidak dianggap "praktik buruk"? Karena Anda hanya memeriksa errnodi akhir, Anda hanya tahu bahwa salah satu fungsi melakukan gagal, tetapi tidak yang berfungsi gagal. Apakah sekadar mengetahui bahwa sesuatu gagal cukup baik untuk sebagian besar program praktis?

Saya mencoba mencari berbagai dokumen Dasar Pemikiran, tetapi banyak dari mereka tidak memiliki banyak detail <errno.h>.

Drew McGowen
sumber
1
Saya kira itu "tidak membayar untuk apa yang tidak Anda inginkan". Jika Anda peduli errno, Anda selalu dapat mengaturnya ke nol sendiri.
Kerrek SB
2
Hal lain yang perlu diperhatikan adalah bahwa suatu fungsi dapat diatur errnoke nilai bukan nol bahkan jika itu berhasil. (Mungkin menyebut beberapa fungsi lain yang gagal, tetapi itu bukan merupakan kegagalan dalam fungsi luar.)
Keith Thompson

Jawaban:

10

Perpustakaan C tidak diatur errnoke 0 karena alasan historis 1 . POSIX tidak lagi mengklaim perpustakaannya tidak akan mengubah nilai jika berhasil, dan halaman manual Linux baru untukerrno.h mencerminkan hal ini:

File <errno.h>header mendefinisikan variabel integer errno, yang diatur oleh panggilan sistem dan beberapa fungsi perpustakaan jika terjadi kesalahan untuk menunjukkan apa yang salah. Nilainya signifikan hanya ketika nilai kembali panggilan menunjukkan kesalahan (yaitu, -1dari sebagian besar panggilan sistem; -1atau NULLdari sebagian besar fungsi perpustakaan); fungsi yang berhasil adalah diizinkan untuk perubahan errno.

The ANSI C Dasar Pemikiran menyatakan bahwa panitia merasa itu lebih praktis untuk mengadopsi dan standarisasi praktek yang ada menggunakan errno.

Mesin pelaporan kesalahan yang berpusat pada pengaturan errnoumumnya dianggap dengan toleransi yang terbaik. Ini memerlukan `` penggabungan patologis '' antara fungsi-fungsi perpustakaan dan memanfaatkan sel memori statis yang dapat ditulisi, yang mengganggu pembangunan perpustakaan yang dapat dibagikan. Namun demikian, Komite lebih memilih untuk menstandarkan mesin yang ada, namun kekurangan ini, daripada menciptakan sesuatu yang lebih ambisius.

Hampir selalu ada cara untuk memeriksa kesalahan di luar memeriksa jika telah errnodiatur. Memeriksa apakah telah errnoditetapkan tidak selalu dapat diandalkan, karena beberapa panggilan memerlukan panggilan API terpisah untuk mendapatkan alasan kesalahan. Misalnya, ferror()digunakan untuk memeriksa kesalahan jika Anda mendapatkan hasil pendek dari fread()atau fwrite().

Cukup menarik, contoh Anda menggunakan strtod()adalah salah satu kasus di mana pengaturan errnoke 0 sebelum panggilan diperlukan untuk mendeteksi dengan benar jika kesalahan telah terjadi. Semua fungsi strto*()string ke angka memiliki persyaratan ini, karena nilai kembali yang valid dikembalikan bahkan dalam menghadapi kesalahan.

errno = 0;
char *endptr;
double x = strtod(str1, &endptr);
if (endptr == str1) {
    /*...parse error */
} else if (errno == ERANGE) {
    if (x == 0) {
        /*...underflow */
    } else if (x == HUGE_VAL) {
        /*...positive overflow */
    } else if (x == -HUGE_VAL) {
        /*...negative overflow */
    } else {
        /*...unknown range error? */
    }
}

Kode di atas didasarkan pada perilaku strtod()seperti yang didokumentasikan di Linux . Standar C hanya menetapkan bahwa underflow tidak dapat mengembalikan nilai lebih besar dari positif terkecil double, dan apakah atau tidak errnodiatur untuk ERANGEimplementasi didefinisikan 2 .

Sebenarnya ada tulisan penasehat sertifikat yang luas yang merekomendasikan selalu menetapkan errnoke 0 sebelum panggilan perpustakaan dan memeriksa nilainya setelah panggilan menunjukkan kegagalan telah terjadi . Ini karena beberapa panggilan pustaka akan disetel errnomeskipun panggilan itu sendiri berhasil 3 .

Nilai errno0 adalah pada startup program, tetapi tidak pernah diatur ke 0 oleh fungsi perpustakaan apa pun. Nilai errnodapat diatur ke nol oleh panggilan fungsi perpustakaan apakah ada atau tidak kesalahan, asalkan penggunaan errnotidak didokumentasikan dalam deskripsi fungsi dalam Standar C. Penting bagi suatu program untuk memeriksa konten errnohanya setelah kesalahan dilaporkan. Lebih tepatnya, errnobermakna hanya setelah fungsi pustaka yang menetapkan errnokesalahan telah mengembalikan kode kesalahan.


1. Sebelumnya saya mengklaim itu untuk menghindari kesalahan dari panggilan sebelumnya. Saya tidak dapat menemukan bukti untuk mendukung klaim ini. Saya juga punya printf()contoh palsu .
2. Terima kasih kepada @chux karena telah menunjukkan ini. Referensi adalah C.11 §7.22.1.3 ¶10.
3. Ditunjukkan oleh @KeithThompson dalam komentar.

jxh
sumber
Masalah kecil: tampaknya underflow dapat menghasilkan hasil non-0: "fungsi mengembalikan nilai yang besarnya tidak lebih besar dari angka positif terkecil yang dinormalisasi dalam jenis pengembalian" C11 7.22.1.3.10.
chux - Reinstate Monica
@ chux: Terima kasih. Saya akan mengedit. Karena pengaturan errnountuk ERANGEadalah implementasi didefinisikan dalam kasus underflow, sebenarnya tidak ada cara yang portabel untuk mendeteksi underflow. Kode saya mengikuti apa yang saya temukan di halaman manual Linux di sistem saya.
jxh
Komentar Anda tentang strto*fungsi - fungsi itulah sebabnya saya bertanya apakah contoh saya akan dianggap sebagai praktik yang buruk, tetapi itu satu-satunya cara di mana errnotidak disetel ke nol akan bermanfaat, atau bahkan berlaku.
@DrewMcGowen: Saya kira satu-satunya poin yang dapat Anda ambil adalah yang errnodapat diatur bahkan dalam kasus keberhasilan, jadi hanya menggunakan fakta yang ditetapkan tidak benar-benar indikator yang cukup baik bahwa kesalahan terjadi. Anda harus memeriksa hasil panggilan individu untuk mengetahui apakah ada kesalahan sama sekali.
jxh
1

Anda dapat melakukan pemeriksaan kesalahan untuk kedua panggilan fungsi, jika Anda benar-benar peduli.

errno = 0;
double x = strtod(str1, NULL);
if (errno)
    // strtod"  failed
else
    // "strtod" succeeded

long y = strtol(str2, NULL);
if (errno)
    // "strtol" failed
else
    // "strtol" succeeded

Karena kita tidak pernah tahu bagaimana fungsi mach dipanggil dalam suatu proses, bagaimana lib dapat mengatur errnos untuk setiap panggilan fungsi, Benar?


sumber
1

Secara sewenang-wenang mengubah errorno analog dengan 'menangkap dan menelan' pengecualian. Sebelum ada pengecualian yang akan menyebar melalui lapisan yang berbeda dari suatu program dan akhirnya mencapai titik di mana penelepon akan menangkap dan menanggapi pengecualian dengan cara tertentu atau dengan sederhana melewati tanggung jawab, ada errnos. Tidak mengubah kesalahan, kecuali jika Anda entah bagaimana menangani, mengesampingkan, memperlakukan sebagai tidak relevan, kesalahan, sangat penting untuk paradigma propagasi penanganan kesalahan generasi pertama ini.

Andyz Smith
sumber