【问题标题】:ftell at a position past 2GBftell 在超过 2GB 的位置
【发布时间】:2013-05-17 18:32:03
【问题描述】:

在 32 位系统上,如果以二进制模式打开的文件的当前位置指示器超过 2GB 点,ftell 会返回什么?在 C99 标准中,这是 未定义的行为,因为 ftell 必须返回 long int(最大值为 2**31-1)?

【问题讨论】:

  • 如果如您所说,这是未定义的行为,则无法(安全或可靠地)分辨。它可以只返回正确的数字,-1, 0,一个随机数,它可以是段错误,或者它可以发送一个特警队。

标签: c file-io c99 stdio ftell


【解决方案1】:

长整数

long int 应该至少为 32 位,但 C99 标准并未将其限制为 32 位。 C99 标准确实提供了方便的类型,例如 int16_tint32_t 等,它们映射到目标平台的正确位大小。

在 ftell/fseek 上

ftell()fseek() 在绝大多数 32 位架构系统上都被限制为 32 位(包括符号位)。所以当有大文件支持时,你会遇到这个 2GB 的问题。

fseekftell 的 POSIX.1-2001 和 SysV 函数是 fseekoftello,因为它们使用 off_t 作为偏移量的参数。

您确实需要使用 -D_FILE_OFFSET_BITS=64 定义编译或在包含 stdio.h 之前在某处定义它以确保 off_t 是 64 位。

cert.org secure coding guide阅读有关此内容。

关于 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 只是一个long,这将生成一个 17GB 的文件并在其末尾粘贴一个 "\nhello world\n"。您可以通过简单地使用 tail -n1 test.out 或显式使用来验证是否存在:

dd if=test.out skip=$((1

请注意,dd 通常使用(1 &lt;&lt; 9) 的块大小,因此34 - 9 = 25 将转储出'\nhello world\n'

【讨论】:

  • 抱歉,您是说 64 而不是 34?由于 long 类型已签名,文件不会是 17GB,而是 9GB,对吗?
  • 不适用于所有x86_64 拱门。具体来说,64 位窗口上的 long 大小仍然是 32 位。
【解决方案2】:

至少在 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 Std 1003.1, 2013 Edition ftell() 应返回-1 并将errno 设置为EOVERFLOW 在这种情况下:

溢出

对于 ftell(),当前文件偏移量无法在 long 类型的对象中正确表示。

【讨论】:

  • 有人说ftell 在这种情况下总是返回0,但是溢出似乎意味着负返回的可能性。 C99 标准中是否保证溢出会“环绕”,还是由实现决定?
  • @VilhelmGray:我怀疑它会返回0,我认为这没有任何意义。可能整个事情都会遇到UB?只是避免它!然而 UB 可能导致任何事情......它也可能返回 42 ! ;-)
  • 我认为它确实归结为 UB。在 C99 标准 (ISO/IEC 9899:TC3) 的 第 6.5 节第 5 段中:If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.
  • @VilhelmGray:感谢您查找标准以证明这一点...... :-)
  • 但是,标准中是否有任何内容暗示ftell 本身会导致溢出——或者它为这些情况返回的值是否未指定?
【解决方案3】:

C99 标准中没有 64b 感知方法。您使用的是什么操作系统/环境?在 Windows 上,有_ftelli64

其他平台看http://forums.codeguru.com/showthread.php?277234-Cannot-use-fopen()-open-file-larger-than-4-GB

【讨论】:

  • ftell 是 C89 和 C99 标准
  • @AhmedMasud:我觉得这里有一个误解。我怀疑“C99 中没有这样的方法 ...”指的是 _ftelli64()notftell()
  • @alk 我在 OP 问题中没有看到对 _ftelli64() 的引用,所以我仍然不明白您的回答有何贡献。
  • @Yossarian 您的答案仍然不正确.. 在 64 位系统上 long int 是 64-bit ; C99 只要求 long int 至少为 32 位,它对其最大大小没有限制,在 64 位系统上为 long,因此 ftell() 偏移量为 64 位
  • @AhmedMasud:请原谅,但要求long至少有 32 位长”确实意味着它可以正好 32位长。 没有必要让它成为 64 位。我也确实同意你的观点,对于 64 位操作系统来说,long 的长度小于 64 位会很奇怪...... ;-) 但是,从我的论证中得出结论,答案是正确,不是吗?
【解决方案4】:

这对我在 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

每个细节、宏、编译器选项都很重要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    • 2013-03-25
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多