Manページfclose(3)
によると:
戻り値
正常に完了すると、0が返されます。それ以外の場合は、
EOF
が返され、グローバル変数errno
がエラーを示すように設定されます。いずれの場合も、ストリームへのそれ以上のアクセス(fclose()
への別の呼び出しを含む)は、未定義の動作になります。エラー
EBADF
fp
の基礎となるファイル記述子が無効です。
fclose()
関数も失敗し、ルーチンclose(2)
、write(2)
、またはfflush(3)
に指定されたエラーのいずれかにerrno
を設定する場合があります。
もちろん、fclose(NULL)
は失敗するはずですが、セグメンテーション違反で直接死ぬのではなく、通常はerrno
で戻ると思います。この動作の理由はありますか?
前もって感謝します。
更新:ここにコードを配置します(特にstrerror()
を試しています)。
FILE *not_exist = NULL;
not_exist = fopen("nonexist", "r");
if(not_exist == NULL){
printError(errno);
}
if(fclose(not_exist) == EOF){
printError(errno);
}
fclose
は、その引数として、FILE
、標準ストリームfopen
、stdin
、またはstdout
のいずれか、またはその他の実装定義の方法で取得されたstderr
ポインターを必要とします。ヌルポインタはこれらの1つではないため、fclose((FILE *)0xdeadbeef)
のように、動作は未定義です。 NULLは特別ではありません Cで;有効なポインタと等しくないことを比較することが保証されているという事実を除けば、他の無効なポインタと同じであり、それを使用すると、NULL
が持つコントラクトの一部としてドキュメントに渡すインターフェイスを除いて、未定義の動作が呼び出されますそれには特別な意味があります。
さらに、エラーを返すことは有効ですが(動作はとにかく未定義であるため)、実装にとって有害な動作です。これは、未定義の動作を非表示にするためです。未定義の動作を呼び出すことの望ましい結果は、エラーを強調表示して修正できるため、常にクラッシュです。 fclose
のほとんどのユーザーはエラーの戻り値をチェックしません。また、ほとんどの人がNULL
をfclose
に渡すほど愚かで、fclose
の戻り値をチェックするほど賢くはないだろうと思います。最終フラッシュが失敗する可能性があるため、人々shouldは一般にfclose
の戻り値をチェックするという議論をすることができますが、これは次の目的でのみ開かれるファイルには必要ありません。読み取り、またはfflush
がfclose
の前に手動で呼び出された場合(ファイルを開いたままでエラーを処理する方が簡単なので、とにかく賢いイディオムです)。
fclose(NULL)
should成功します。 free(NULL)
は成功します。これにより、クリーンアップコードの記述が容易になります。
残念ながら、それはそれが定義された方法ではありません。したがって、ポータブルプログラムでfclose(NULL)
を使用することはできません。 (例: http://pubs.opengroup.org/onlinepubs/9699919799/ を参照)。
他の人が述べているように、間違った場所にNULLを渡しても、通常はエラーが返されることは望ましくありません。少なくともデバッグ/テストビルドでは、警告メッセージが必要です。 NULLを逆参照すると、即時の警告メッセージが表示され、プログラミングエラーを識別するバックトレースを収集する機会が得られます:)。あなたがプログラミングしている間、セグメンテーション違反はあなたが得ることができる最高のエラーについてです。 Cにはさらに多くの微妙なエラーがあり、デバッグにはるかに時間がかかります...
エラーリターンを悪用して、プログラミングエラーに対する堅牢性を高めることができます。ただし、ソフトウェアのクラッシュによってデータが失われることが心配な場合は、まったく同じことが発生する可能性があることに注意してください。ハードウェアの電源が切れた場合。これが自動保存がある理由です( exとviのような2文字の名前を持つUnixテキストエディタ )。一貫性のない状態で続行するよりも、ソフトウェアが目に見えてクラッシュする方が望ましいでしょう。
マニュアルページが話しているエラーは、プログラミングエラーではなく、ランタイムエラーです。ポインタを期待しているAPIにNULL
を渡すだけで、そのAPIが合理的なことをすることを期待することはできません。データへのポインタを要求するように文書化された関数にNULL
ポインタを渡すことはバグです。
関連する質問: CまたはC++のいずれかで、ポインターパラメーターをNULL/nullptrに対してチェックする必要がありますか?
引用するには R。のコメント その質問への回答の1つについて:
...オペレーティング環境の例外的な条件(fsがいっぱい、メモリ不足、ネットワークのダウンなど)から発生するエラーとプログラミングエラーを混同しているようです。前者の場合、もちろん、堅牢なプログラムがそれらを適切に処理できる必要があります。後者の場合、堅牢なプログラムではそもそもそれらを体験できません。
このfclose()
の問題は、FreeBSDの遺産のようであり、MicrosoftとLinuxの両方の陣営によって無批判に受け入れられました。
ただし、HP、SGI、Solaris、およびCYGWINは、すべてfclose(NULL)
を適切に処理します。例えば、 man fclose
OPのglibcではなくnewlibを使用するCYGWINの場合、次のように述べています。
fclose returns 0 if successful (including when FP is NULL or not an open file)
関連する議論については https://stackoverflow.com/a/8442421/318716 を参照してください。
マンページは、基礎となるファイル記述子(open
を呼び出したときにfopen
システムコールを介して内部的に取得されるもの)が無効であることについて説明していると思います。 ファイルポインタこれをfclose
に渡します。