9

我有一个程序可以打开一个文件并检查它的长度。

FILE* fd = fopen(argv[1], "rb");
fseek(fd, 0, SEEK_END);
size_t flen = ftell(fd);
if (flen == ((size_t)-1)) {
    printf("%s is a directory.\n", argv[1]);
    fclose(fd);
    exit(1);
}

现在,至少在 Linux 下,fopen()打开目录时会返回一个有效的文件描述符。这导致查找操作返回-1(或者,在 64 位系统上size_t,无符号0xFFFFFFFFFFFFFFFF=2 64 -1)。

不幸的是,上面代码 ( flen == ((size_t)-1)) 中的条件并没有捕捉到这种情况,也没有flen == 0xFFFFFFFF(编辑:应该是0xFFFFFFFFFFFFFFFF)。printf()- 以%xord%d作为格式字符串的命令显示比较的双方应该具有相同的值。

为什么比较运算符的行为如此奇怪,即使双方都是相同的类型(size_t)?我使用 gcc 4.8.1 作为编译器。

4

2 回答 2

9

来自http://pubs.opengroup.org/onlinepubs/7908799/xsh/fopen.html

The fopen() function will fail if: 
[EISDIR] The named file is a directory and mode requires write access.

至少在 Linux 上,如果您尝试fopen("dirname", "wb")遇到 EISDIR 错误。我还尝试使用具有 d-------- 访问权限的目录,但我仍然得到 EISDIR(而不是 EACCES。)

于 2014-05-30T01:47:05.847 回答
8

C99 标准(或 C2011 标准)中不存在目录。因此,根据定义,fopen-ing 目录要么是特定于实现的行为,要么是未定义的行为。

fopen(3)可能会失败(给出NULL结果)。fseek(3)也可能失败(通过返回 -1)。然后你最好检查errno(3)或使用perror(3)

ftell记录在案以返回 a long,并且-1L在失败时返回。在 64 位 Linux 上,这是0xffffffffffffffff.

你的代码应该是

FILE* fd = fopen(argv[1], "rb");
if (!fd) 
  { perror(argv[1]); exit(EXIT_FAILURE); };
if (fseek(fd, 0, SEEK_END)<0) 
  { perror("fseek"); exit(EXIT_FAILURE); };
long flen = ftell(fd);
if (flen == -1L)
  { perror("ftell"); exit(EXIT_FAILURE); };

顺便说一句,在具有 libc-2.17 和 3.10.6 内核的 Linux/Debian/Sid/AMD64 上,该代码在运行时运行argv[1]正常/tmp;令人惊讶的flen是,LONG_MAX0x7fffffffffffffff

顺便说一句,在 Linux 上,目录是文件的一种特殊情况。在文件路径上使用stat(2)fstat (以及在文件描述符上,可能使用fileno(3)从 some获得FILE*)以了解有关某个文件的更多元数据,包括其“类型”(通过其模式)。您希望opendir(3)readdir(3)closedir(3)对目录内容进行操作。另请参见inode(7)

于 2013-08-12T17:48:03.810 回答