对这段代码的完整解释需要一段时间。
简短的版本是:
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 *
返回打开文件的(文件流指针)。因此,让我们在此基础上批评代码。
函数原型行OK;可能是char const catalogname[]
强调该函数不会修改文件名,但这是一种改进,而不是修复错误的更改。
catalogname
函数中的类型是char *
。在函数的参数列表中,数组大致等同于指针。我通常会写信FILE *openCatalog(char const *catalogname)
强调在函数中,它是一个char const *
变量。尽管如此,使用您使用的数组表示法是 100% 合法的;对我来说,使用指针符号纯粹是一种风格偏好。
下一个可执行行有几个问题。函数调用在fopen()
技术上并没有错,尽管它打开的是一个具有固定名称的文件,*catalogname
而不是变量中指定的文件catalogname
。要解决这个问题,您需要删除引号。会给你一个字符而*
不是字符串,它将是文件名的第一个字符。所以,*
也删除。
这让您fopen()
返回一个值,实际上是 a FILE *
,然后您将它分配给catalogname
, a char *
。这是类型不匹配,编译器会发出警告。如第一次重写所示,更正常的处理方法是:
FILE *fp = fopen(catalogname, "r");
这解释了您的错误消息:
a3.c:20: warning: assignment from incompatible pointer type
我们不知道您的目录是文本文件还是二进制文件。如果它是一个二进制文件,那么"rb"
如果你在 Windows 上(这真的很重要),你应该使用它来打开它,这在类 Unix 系统上也可以正常工作(文本和二进制文件之间没有区别)。
代码中的下一行是条件:
if (fopen != 0)
这实际上检查了函数的函数指针是否fopen()
为空。并且 C 标准保证在托管环境(您正在使用)中,没有函数指针将为空。因此编译器可以优化该测试以假设条件始终为真。
你真正需要的是对结果的测试:
if (fp != 0)
然后有两个返回语句,一个返回 1,一个返回 0。返回 1 的一个会引发关于将整数转换为指针的警告,因为该函数被定义为返回 a FILE *
,而 1 是一个整数。
这说明了您的其他警告消息:
a3.c:22: warning: return makes pointer from integer without a cast
返回 0 的返回不会产生警告,因为 0 是一个空指针常量,并且对于这样的函数返回是一个有效值。
代码可能应该简单地从fopen()
. 在某个地方测试值是正确的,无论是在这个函数中还是在调用函数中,以确保打开成功。你可以在这里测试它:
if (fp == 0)
err_report_and_exit("Failed to open file %s (%d: %s)\n",
catalogname, errno, strerror(errno));
使用errno
andstrerror()
意味着您还应该包含标题:
#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);
}
如果该函数旨在检查文件是否可以打开以进行读取,则上述问题大致相同。我首先展示的修改后的代码有点冗长。由于假定的目的是检查文件是否可以打开,所以调用代码应该负责处理“无法打开的情况”;它知道要生成什么样的诊断。这是修订版的稍微紧凑的版本,但是 this 和上面的目标代码是相同的。
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_
- 尽管输入的解释比修复它需要更长的时间。大包包含在 filesstderr.c
和stderr.h
中,尽管有一个论点是它使用前缀如履薄冰std
。然而,它是我的标准错误报告包。)