在 32 位系统上,ftell
如果以二进制模式打开的文件的当前位置指示器超过 2GB 点,会返回什么?在C99标准中,这种未定义的行为ftell
是否必须返回long int
(最大值为2**31-1
)?
4 回答
长整数
long int
应该是至少 32 位,但 C99 标准并不将其限制为 32 位。C99 标准确实提供了方便的类型,如int16_t
&int32_t
等,这些类型映射到目标平台的正确位大小。
在 ftell/fseek 上
ftell()
并且fseek()
在绝大多数 32 位架构系统上仅限于 32 位(包括符号位)。因此,当有大文件支持时,您会遇到这个 2GB 的问题。
POSIX.1-2001 和 SysV 函数用于和fseek
是ftell
因为它们使用 off_t 作为偏移量的参数。fseeko
ftello
您确实需要-D_FILE_OFFSET_BITS=64
在包含 stdio.h 之前定义 compile with 或在某处定义它以确保它off_t
是 64 位的。
在cert.org 安全编码指南中阅读有关此内容的信息。
关于 ftell 和 long int 大小的混淆
C99 说long int
必须至少是32 位它没有说它不能更大
在 x86_64 架构上尝试以下操作:
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE *fp;
fp = fopen( "test.out", "w");
if ( !fp )
return -1;
fseek(fp, (1L << 34), SEEK_SET);
fprintf(fp, "\nhello world\n");
fclose(fp);
return 0;
}
请注意,这1L
只是 a long
,这将生成一个 17GB 的文件并将 a 粘贴"\nhello world\n"
到它的末尾。您可以通过简单地使用tail -n1 test.out
或显式使用来验证是否存在:
dd if=test.out skip=$((1 << 25))
请注意, dd 通常使用块大小,(1 << 9)
因此 34 - 9 = 25
将转储'\nhello world\n'
至少在 32 位操作系统上ftell()
,它会溢出或出错,或者只是遇到未定义的行为。
为了解决这个问题,您可能喜欢使用off_t ftello(FILE *stream);
and #define _FILE_OFFSET_BITS 64
。
逐字逐句man ftello
:
fseeko() 和 ftello() 函数分别与 fseek(3) 和 ftell(3) 相同(参见 fseek(3)),除了 fseeko() 的偏移量参数和 ftello() 的返回值是键入 off_t 而不是 long。
在许多架构上,off_t 和 long 都是 32 位类型,但编译时使用
#define _FILE_OFFSET_BITS 64
会将 off_t 转换为 64 位类型。
更新:
根据IEEE Std 1003.1,2013 版 在这种情况下ftell()
应返回-1
并设置errno
为:EOVERFLOW
溢出
对于 ftell(),当前文件偏移量无法在 long 类型的对象中正确表示。
C99 标准中没有 64b 感知方法。您使用的是什么操作系统/环境?在 Windows 上,有_ftelli64
.
在其他平台上,请查看http://forums.codeguru.com/showthread.php?277234-Cannot-use-fopen()-open-file-larger-than-4-GB
这适用于我在 Windows32/MinGW 上玩 6GB 文件
#define _FILE_OFFSET_BITS 64
#include<stdio.h>
int main() {
FILE *f = fopen("largefile.zip","rb");
fseeko64(f, 0, SEEK_END);
off64_t size = ftello64(f);
printf("%llu\n", size);
}
gcc readlargefile.c -c -std=C99 -o readlargefile.exe
每一个细节、宏、编译器选项都很重要。