私はTechnionのCS学生ですが、errno
変数とcスタイルの関数呼び出しについて学びました。これは、cスタイルのシステムコールがレジスタを使用して値を返す場合、なぜ誰かが使用する必要があるのか疑問に思いerrno
ます。
4 に答える
を使用する主な理由errno
は、エラー状態に関する詳細情報を提供することです。
これは、関数の可能な戻り値のほとんど(またはすべて)が実際に有効な戻り値である状況で特に役立ちます。
fopen()
へのポインタを返す関数について考えてみますFILE
。を除いて、すべての可能な戻り値も有効な戻り値ですNULL
。したがって、失敗するとfopen()
戻りますNULL
。しかし、その場合、関数が失敗した原因を正確に知ることはできません。したがって、fopen()
を使用errno
して正確なエラー状態を示します。つまり、ファイルが存在しないか、ファイルを読み取る権限がないか、システムのメモリが不足しています。
errno
グローバル変数(スレッドが普及するまではグローバル変数でした)と考えることができます。現在errno
は、通常、エラー状態を返す関数呼び出しをラップするマクロです。しかし、これはスレッド固有のグローバル変数を実装するCの方法にすぎません。
の代替手段errno
はあまり快適ではありません:
へのポインタを関数に指定するint
と、関数はそのエラー状態をそこに格納できます。strtod()
このテクニックの良い例です。ただし、これによりAPIがより複雑になるため、あまり望ましくありません。また、プログラマーに新しいを定義するように強制しますint
。これは、関数が失敗しても気にしない場合は面倒です。
複数の戻り値を許可する(そして例外を備えていない)言語では、2つの値を返すのが一般的です。1つは実際の結果用で、もう1つはエラー状態を示します。Goのような言語では、次のようなコードが表示されます。
result, ok = foo();
if (ok) {
// handle error denoted by "ok"
}
errno
それが「古い」技術であり、したがって避けるべきであると主張する人々を信用しないでください。あなたがプログラミングしているマシンはCよりもはるかに古く、errno
Cよりもはるかに古く、誰もそれについて不満を言ったことはありません。
Cライブラリの設計は、初期のUnixと同時にかなり前に行われていました。別のエラーコードを使用することは珍しいパターンではありません(Win32にも同様のGetLastError()があります)。そしてそれは表面的な利点があります。
関数のクラスに一般的に使用される戻り値を持たせたい場合、それを使用してエラーを返すことは簡単にはできません。たとえば、架空のAPIを想像してみてください
mytime_t t = get_current_time();
このAPIの一般的な使用法は、時間を取得することです。ただし、状況によっては失敗する可能性があり、errnoから詳細なエラー情報を取得します。これにより、APIコードは、言う必要がある場合よりも読み取りと書き込みが少し簡単になります。
mytime_t t=0;
errno_t e = get_current_time(&t);
表面的には、errno型システムは魅力的です。ただし、エラー状態を実際の関数呼び出しから分離すると、多くの問題が発生します。最近の環境では、errno_tはスレッドごとの変数になります(最も明白な問題の原因を取り除く)が、それでも問題に直面します。
mytime_t t = get_current_time();
mysize_t s = get_window_size();
次に、最初の関数からのerrnoを目に見えない形で破棄します。コードがエラーパスで実行される可能性がある場合、または1つの関数が他の関数に関して実装されている場合、これはさらに複雑になります。errno値を保存および復元することで、確実になります。そのようなシステムは壊れやすく、望ましくないので、今ではかなり受け入れられていると思います。
多くの人々は、明示的なパラメーター/リターンセットの外部でエラーを運ぶ例外を使用します(多くのC ++プログラマーがこの選択を行います)。例外のない言語または環境で作業する人々は、弾丸を噛み、エラーの戻り値を予約し、常にパラメーターを介して出力を提供する傾向があります(COMはHRESULTを使用してこの選択を行います)。
errno
おそらく今日では誰もそのように設計しない歴史的なインターフェースであるという点で、これは複雑なことです。さらに、ほとんどのシステムでは、今日では変数のように見えるだけで、変数ではありません。通常、これは関数呼び出しを非表示にするマクロとして実装され、その関数呼び出しはスレッド固有のエラー状態を返します。
私が考えることができる最良の例は、fopen
失敗時にNULLを返すstdio関数であり、失敗した理由を見つける唯一の方法は、errnoやperrorを使用することです。
他の例があるはずです。これが今頭に浮かんだことです。