_file_size 在64位程序中仍返回-1,因其内部用32位long存储大小,调用GetFileSize后强制拼接导致溢出;应改用GetFileSizeEx或跨平台_stat64。

为什么 _file_size 在 64 位程序里也返回 -1?
因为 _file_size 是 Microsoft C 运行时的旧函数,内部用 long 存大小,而 long 在 Windows 上始终是 32 位(即使在 x64 编译下),最大只支持 2GB(有符号)或 4GB(无符号),超过就溢出变负数或截断。这不是编译选项或平台位数能绕过的限制。
- 它不读文件系统元数据,而是打开文件后调用
GetFileSize(Win32 API),再把DWORD高低两部分强行拼成long - 哪怕你用
/D "_USE_32BIT_TIME_T=0"或/D "_CRT_SECURE_NO_WARNINGS",也改不了它的返回类型和底层逻辑 - 对大于 4GB 的文件,典型错误现象是返回
-1,且errno通常为EOVERFLOW
Windows 下真正可靠的方式:用 GetFileSizeEx
这是 Win32 原生支持大文件的 API,返回 LARGE_INTEGER(即 int64_t),能准确表示最大约 9EB 的文件大小。必须用它替代 _file_size。
- 需要先用
CreateFile打开文件,注意加FILE_FLAG_BACKUP_SEMANTICS(对目录也有效)和GENERIC_READ权限 GetFileSizeEx第二个参数是指向LARGE_INTEGER*的指针,别传int*或long long*—— 类型不匹配会导致高位丢失- 示例关键片段:
LARGE_INTEGER size; HANDLE h = CreateFile(L"bigfile.bin", GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (h != INVALID_HANDLE_VALUE && GetFileSizeEx(h, &size)) { int64_t bytes = size.QuadPart; // 这才是真实字节数 } CloseHandle(h);
跨平台可移植写法:优先用 stat64 或 stat 的 64 位变体
Linux/macOS 和现代 MinGW、MSVC(带 UCRT)都支持 struct stat64 或 _stat64,它们的 st_size 字段是 off64_t(64 位),不依赖文件句柄。
- MSVC 中:用
_stat64(不是_stat),头文件是sys/stat.h;路径传窄字符(const char*)即可 - MinGW-w64 / Linux:直接用
stat(glibc 默认启用 _FILE_OFFSET_BITS=64),或显式用stat64 - 别混用:比如在 MSVC 下用
struct _stat+_stat,st_size仍是 32 位,照样溢出 - 示例:
struct _stat64 sb; if (_stat64("bigfile.bin", &sb) == 0) { int64_t bytes = sb.st_size; // 安全
容易被忽略的权限与符号链接问题
即使函数本身支持大文件,调用仍可能失败——不是因为大小,而是访问控制或路径语义。
- 在 Windows 上,若文件在 NTFS 压缩/加密卷、重解析点(如符号链接、挂载点)后,
GetFileSizeEx可能返回 0 或失败,需检查GetLastError()是否为ERROR_NOT_SUPPORTED或ERROR_ACCESS_DENIED - 用
_stat64时,默认解析符号链接;如需获取链接本身大小(而非目标),得用_stat64的变体(如 MinGW 的lstat64),但 MSVC 不提供 - 某些网络文件系统(SMB/CIFS)或虚拟文件系统(如 OneDrive 按需文件)可能不报告真实大小,返回 0 或
ENODATA,这时只能靠读取实际内容长度来判断