Cライブラリ関数の背後にある根拠がerrnoをゼロに設定しない


9

C標準では、C標準ライブラリ関数errnoをゼロに設定しないことを義務付けています。なぜこれが正確なのですか?

私はそれがいくつかの関数を呼び出しerrno、最後の関数の後でのみチェックするのに役立つことを理解できました-例えば:

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

しかし、これは「悪い習慣」とは見なされていませんか?あなただけチェックしているので、errno非常に最後に、あなただけの機能の一つであることを知ったの失敗ではなく、どの関数が失敗しました。何かがほとんどの実用的なプログラムに対して十分に失敗したことを単に知っていますか?

さまざまなC Rationaleドキュメントを探してみましたが、それらの多くにはの詳細があまりありません<errno.h>


1
「欲しくないものにお金を払ってはいけない」だと思います。を気errnoにする場合は、いつでも自分でゼロに設定できます。
Kerrek SB 2013

2
他に知っておくべきことは、関数がerrno成功した場合でも、関数をゼロ以外の値に設定できることです。(失敗する他の関数を呼び出す可能性がありますが、それは外部関数の失敗を構成するものではありません。)
キース・トンプソン

回答:


10

errno歴史的な理由により、Cライブラリは0に設定されません1。POSIXは、ライブラリが成功した場合に値を変更しないと主張しなくなりました。新しいLinuxのmanページにerrno.hは、これ反映さています。

<errno.h>ヘッダファイルには、整数型の変数を定義しerrno、システムコールと何が悪かったのかを示すためにエラーが発生した場合に、いくつかのライブラリ関数によって設定されています、。その値は、呼び出しの戻り値がエラーを示した場合(つまり、-1ほとんどのシステムコールから、-1またはNULLほとんどのライブラリ関数から)にのみ意味があります。成功した関数変更できますerrno

ANSI C理由は、委員会は、使用して、既存の慣行を採用し、標準化する方が実用的だと感じたと述べていますerrno

の設定を中心とするエラー報告機構errnoは、一般的には最高でも許容範囲であると見なされます。ライブラリ関数間の「病理学的結合」が必要であり、静的な書き込み可能なメモリセルを利用するため、共有可能なライブラリの構築が妨げられます。それにもかかわらず、委員会は、より野心的な何かを発明するのではなく、この既存の、しかし不十分な機械を標準化することを選びました。

ほとんどの場合、errno設定されているかどうかを確認する以外に、エラーを確認する方法があります。errno一部の呼び出しでは、エラーの理由を取得するために別のAPIを呼び出す必要があるため、設定されたかどうかの確認は常に信頼できるとは限りません。たとえばferror()fread()またはから短い結果が得られた場合にエラーをチェックするために使用されますfwrite()

興味深いことに、使用例は、エラーが発生したかどうかを正しく検出するために、呼び出しの前に0にstrtod()設定errnoする必要がある場合の1つです。strto*()エラーが発生した場合でも有効な戻り値が返されるため、すべての文字列から数値への関数にはこの要件があります。

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? */
    }
}

上記のコードはstrtod()、Linuxに記載されているの動作に基づいています。C規格では、アンダーフローが最小の正より大きい値を返すことはできず、に設定されているdoubleかどうかは実装定義2であると規定されています。errnoERANGE

実際には、ライブラリの呼び出しの前に常に0に設定し、呼び出しが失敗したことを示した後にその値を確認することをお勧めする、広範な証明書勧告の書き込みがあります。いくつかのライブラリ呼び出しが設定されますので、これはあるコール自体が成功した場合でも、3errnoerrno

の値はerrnoプログラムの起動時には0ですが、ライブラリ関数によって0に設定されることはありません。C規格の関数の説明にerrno使用がerrno記載されていない場合、エラーの有無にかかわらず、ライブラリ関数呼び出しによっての値をゼロ以外に設定できます。プログラムがerrnoエラーを報告した後でのみ内容を検査することは意味があります。より正確にerrnoerrno、エラーを設定するライブラリ関数がエラーコードを返した後にのみ意味があります。


1.以前は、以前の呼び出しからのエラーをマスクしないようにするためだと主張しました。この主張を裏付ける証拠は見つかりません。私も偽のprintf()例を持っています。
2.これを指摘してくれた@chuxに感謝します。参照はC.11§7.22.1.3¶10です。
3.コメントで@KeithThompsonによって指摘されました。


軽微な問題:アンダーフローが0以外の結果になる可能性があるようです:「関数は、戻り値の型で最小化された正の数よりも大きくない値を返します」C11 7.22.1.3.10。
chux-モニカを2013年

@chux:ありがとう。編集します。errnoへの設定ERANGEはアンダーフローの場合に定義された実装であるため、実際にはアンダーフローを検出する移植可能な方法はありません。私のコードは、システムのLinux manページで見つけたものに従いました。
jxh 2013

strto*関数についてのコメントは、私の例が悪い習慣と見なされるかどうかを尋ねた理由ですが、それerrnoがゼロに設定されていないことが役立つ、または適用される唯一の方法です。

@DrewMcGowen:あなたが取ることができる唯一のポイントはerrno、成功した場合でも設定される可能性があるということなので、設定されたという事実を使用するだけでは、エラーが発生したことを示す十分な指標ではありません。エラーが発生したかどうかを確認するには、個々の呼び出し自体の結果を確認する必要があります。
jxh 2013

1

本当に気になる場合は、両方の関数呼び出しのエラーチェックを実行できます。

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

mach関数がプロセスでどのように呼び出されたかはわからないので、libは各関数呼び出しのerrnoをどのように設定できますか?


1

errornoを任意に変更することは、例外を「キャッチして飲み込む」ことに似ています。プログラムのさまざまな層を介して伝播し、最終的に呼び出し元が何らかの方法で例外をキャッチして応答するか、単純にドルを渡す点に到達する例外が発生する前に、errnoがありました。何らかの方法でエラーを処理、オーバーライド、処理する場合を除いて、errnoを変更しないことは、この第1世代のエラー処理伝播パラダイムに不可欠です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.