このコードの完全な解説にはしばらく時間がかかります。
簡単なバージョンは次のとおりです。
FILE *openCatalog(char catalogname[])
{
FILE *fp = fopen(catalogname, "r");
return(fp);
}
これにより、指定されたファイルが読み取り用に開かれ、ファイル ストリーム ポインターが返されます。
別の簡単なバージョンでは、ファイルを開くことができるかどうかを確認します。
int openCatalog(char catalogname[])
{
FILE *fp = fopen(catalogname, "r");
if (fp != 0)
{
fclose(fp);
return 1;
}
else
{
return 0;
}
}
釈義
元のコードは次のとおりです。
FILE* openCatalog(char catalogname[])
{
catalogname = fopen ("*catalogname", "r");
if(fopen != 0)
{
return 1;
}
else
{
return 0;
}
}
「ファイルを開く」以上の仕様はありません。関数のシグネチャを考えるとFILE *
、開いているファイルの (ファイル ストリーム ポインター) を返したいようです。それでは、それを基にコードを批評しましょう。
関数プロトタイプ行は問題ありません。関数がファイル名を変更しないことを強調することもできますがchar const catalogname[]
、これは改良であり、バグ修正の変更ではありません。
catalogname
関数の の型は ですchar *
。関数の引数リストでは、配列は大まかにポインターと同等です。FILE *openCatalog(char const *catalogname)
通常、関数内ではchar const *
変数であることを強調するために書きます。それにもかかわらず、使用した配列表記を使用することは 100% 正当です。ポインター表記を使用するのは、純粋に文体上の好みです。
次の実行可能な行には、いくつかの問題があります。への関数呼び出しは、変数で指定されたファイルではなくfopen()
固定名のファイルを開きますが、技術的には間違っていません。これを修正するには、引用符を削除する必要があります。は文字列ではなく文字を提供し、それがファイル名の最初の文字になります。だから、あまりにも削除します。*catalogname
catalogname
*
*
これによりfopen()
、値、実際には a を返すことになり、それをaFILE *
に割り当てます。これは型の不一致であり、コンパイラが警告します。最初の書き換えで示したように、これを処理するより通常の方法は次のようになります。catalogname
char *
FILE *fp = fopen(catalogname, "r");
これはあなたのエラーメッセージを説明します:
a3.c:20: warning: assignment from incompatible pointer type
カタログがテキスト ファイルかバイナリ ファイルかはわかりません。それがバイナリ ファイルである"rb"
場合、Windows を使用している場合 (実際に重要な場合) を使用して開く必要があり、Unix ライクなシステム (テキスト ファイルとバイナリ ファイルの区別がない場合) でも問題なく動作します。
コードの次の行は条件です。
if (fopen != 0)
これは、関数の関数ポインタfopen()
が null かどうかを実際にチェックします。また、C 標準では、(使用している) ホストされた環境では、関数ポインターが null にならないことが保証されています。そのため、コンパイラはそのテストを最適化して、条件が常に真であると想定できます。
実際に必要なのは、結果のテストです。
if (fp != 0)
次に、1 を返すステートメントと 0 を返すステートメントの 2 つの return ステートメントがあります。関数が a を返すように定義されておりFILE *
、1 が整数であるため、1 を返すステートメントは、整数をポインターに変換することに関する警告を引き出します。
これは、他の警告メッセージを説明します。
a3.c:22: warning: return makes pointer from integer without a cast
0 は null ポインター定数であり、このような関数が返す有効な値であるため、0 を返す return は警告を生成しません。
コードは、おそらく単に から値を返す必要がありますfopen()
。この関数または呼び出し関数のいずれかで値をテストして、オープンが成功したことを確認するのは正しいことです。ここで次のようにテストできます。
if (fp == 0)
err_report_and_exit("Failed to open file %s (%d: %s)\n",
catalogname, errno, strerror(errno));
errno
andを使用するとstrerror()
、ヘッダーも含める必要があることを意味します。
#include <errno.h>
#include <string.h>
コードは からファイル ストリームを返す必要がありますfopen()
。
return fp;
合計すると、次のようになります。
FILE *openCatalog(char catalogname[])
{
FILE *fp = fopen(catalogname, "r");
if (fp == 0)
err_report_and_exit("Failed to open file %s (%d: %s)\n",
catalogname, errno, strerror(errno));
return(fp);
}
上記の問題は、関数がファイルを読み取り用に開くことができるかどうかを確認することを目的としている場合、ほとんど同じです。最初に示した修正後のコードは少し冗長です。想定される目的は、ファイルを開くことができるかどうかを確認することであるため、呼び出し元のコードは「開くことができないケース」を処理する責任を負う必要があります。生成する診断の種類を認識しています。これはリビジョンのもう少しコンパクトなバージョンですが、これと上記のオブジェクト コードは同じです。
int openCatalog(char catalogname[])
{
FILE *fp = fopen(catalogname, "r");
if (fp != 0)
{
fclose(fp);
return 1;
}
return 0;
}
の簡単な実装err_report_and_exit()
は次のとおりです。
#include "errreport.h"
#include <stdio.h>
#include <stdlib.h>
#include <starg.h>
void err_report_and_exit(char const *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
「errreport.h」ヘッダーは次のようになります。
#ifndef ERRREPORT_H_INCLUDED
#define ERRREPORT_H_INCLUDED
#ifdef __GNUC__
#define PRINTFLIKE(n,m) __attribute__((format(printf,n,m)))
#define NORETURN() __attribute__((noreturn))
#else
#define PRINTFLIKE(n,m) /* If only */
#define NORETURN() /* If only */
#endif /* __GNUC__ */
extern void err_report_and_exit(char const *format, ...) PRINTFLIKE(1,2) NORETURN();
#endif /* ERRREPORT_H_INCLUDED */
GCC 固有のビットは、直接使用した場合と同じレベルのフォーマット エラー レポートが得られることを意味しますprintf()
。
(このコードは、関数プレフィックスとして体系的に使用するより大きなパッケージをモデルにしています。err_
そのパッケージでは、この関数はerr_error()
.ファイルとに含まれていますが、接頭辞を使用して薄氷を踏んでいるという議論があります. ただし、これは私の標準的なエラー報告パッケージです.)err_
error_
stderr.c
stderr.h
std