在32位系统上,什么是 ftell
返回以二进制模式打开的文件的当前位置指标是否超过了2GB点?在 C99 标准,这是 未定义行为 自从 ftell
必须返回 long int
(最大值为 2**31-1
)?
long int
应该是至少32位的,但C99标准并没有限制它为32位。C99标准确实提供了方便类型,比如 int16_t
& int32_t
等,映射到目标平台的正确比特大小。
ftell()
和 fseek()
在绝大多数32位架构的系统上都被限制在32位(包括符号位)。所以当有大文件支持的时候就会遇到这个2GB的问题。
POSIX.1-2001和SysV函数,对于 fseek
和 ftell
是 fseeko
和 ftello
因为它们使用 off_t 作为偏移量的参数。
你确实需要用 -D_FILE_OFFSET_BITS=64
或在包含stdio.h之前在某个地方定义它,以确保 off_t
是64位的。
阅读关于这一点在 cert.org安全编码指南.
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
不过是 long
,这将产生一个17GB的文件,并坚持一个。"\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);
和 #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标准1003.1,2013年版 ftell()
将返回 -1
并设置 errno
到 EOVERFLOW
在这种情况下。
EOVERFLOW
对于ftell(),当前的文件偏移量不能正确地用long类型的对象表示。
在C99标准中没有64b感知方法。你使用的是什么OSenvironment?在windows系统中,有一个 _ftelli64
.
在其他平台上,看看 http:/forums.codeguru.comshowthread.php?277234-Cannot-use-fopen()-open-file-larger-than-4B。
这对我来说,在Windows32MinGW上玩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
每一个细节,宏,编译器选项,都很重要。