获取文件大小这个操作在日常开发中出现的频率很高,但真正可靠的实现并不是只有一种。C++标准库没有直接提供获取文件字节数的函数,在POSIX环境中stat系统调用通常是首选。它通过路径直接读取文件在文件系统中的元数据,不需要打开文件、读取内容或移动文件指针,因此不会干扰其他读写操作。调用成功时,stat会把包括权限、时间戳、大小在内的信息写入一个struct stat结构体,其中st_size字段就是普通文件大小。下面逐步拆解stat的用法和几个容易忽略的技巧。

stat的工作机制与核心结构体
stat函数声明在<sys/stat.h>头文件中,原型为int stat(const char *pathname, struct stat *statbuf);。它接收两个参数:目标路径和指向struct stat的指针。执行成功后返回0,失败返回-1并设置errno。常见错误码包括ENOENT表示路径不存在,EACCES表示权限不足,ENOTDIR表示路径前缀不是目录。
struct stat里最重要的是st_mode和st_size。st_mode保存文件类型和权限位,使用S_ISREG(info.st_mode)可以判断目标是否为普通文件。st_size的类型是off_t,在64位Linux下通常为64位有符号整数,能表示超大文件;但在32位平台可能只有32位,超过约2GB会出现截断。解决办法是编译时定义_FILE_OFFSET_BITS=64,让off_t升级为64位。
另一个要区分的概念是逻辑大小与磁盘占用。st_size反映的是文件内容字节数,不包含文件系统为存储该文件额外分配的块或元数据;要查看实际占用可以用st_blocks字段乘以512。对于稀疏文件,两者差异可能很大,这也是stat比遍历内容更直观的原因之一。
用stat获取文件大小的完整示例与错误处理
下面是一个封装好的函数,接收路径参数并输出错误原因。先判断stat调用是否成功,再确认目标是普通文件,最后安全转换成long long返回。错误信息通过strerror输出,比直接打印错误码更直观。
#include <sys/stat.h>
#include <iostream>
#include <cerrno>
#include <cstring>
bool getFileSize(const char* path, long long& size) {
struct stat info;
if (stat(path, &info) != 0) {
std::cerr << "stat failed: " << std::strerror(errno) << "\n";
return false;
}
if (!S_ISREG(info.st_mode)) {
std::cerr << "not a regular file\n";
return false;
}
size = static_cast<long long>(info.st_size);
return true;
}
错误处理不能只判断返回值非0就结束。例如路径不存在、权限不足、目录前缀错误,对应的处理策略完全不同。在记录日志时建议同时保留errno和路径参数,方便线上排查。C++程序里errno是线程本地变量,多线程环境下不会互相污染,但必须在调用失败后立刻读取,否则后续库函数可能覆盖它。
如果希望接口更现代,可以返回std::optional<long long>,用空值表示失败。转换大文件大小时使用static_cast而不是隐式转换,既避免警告,也让类型意图更清晰。对于只读检测,stat不要求文件可读权限,只要路径所在目录有执行权限即可获取元数据。
fstat与lstat:处理已打开文件与符号链接
如果文件已经被程序打开,或只想获取已打开文件的属性,可以使用fstat(int fd, struct stat *buf)。它与stat的区别是第一个参数换成文件描述符。这样能避免路径在检查期间被替换或删除的竞态问题。比如先open得到fd,再用fstat读取大小,然后继续读写,整个过程指向同一个打开文件。
符号链接则更微妙。stat会跟踪符号链接并返回目标文件的信息,而lstat返回链接文件本身的信息。如果只想判断某个路径是否是符号链接以及链接本身大小,可以用S_ISLNK(info.st_mode)配合lstat。在清理目录或做安全检查时,这个差别会直接影响判断结果。
下面的示例分别用lstat检查链接,再用stat获取目标文件大小:
#include <sys/stat.h>
#include <iostream>
void checkLink(const char* path) {
struct stat info;
if (lstat(path, &info) == 0 && S_ISLNK(info.st_mode)) {
std::cout << path << " is a symbolic link\n";
} else if (stat(path, &info) == 0 && S_ISREG(info.st_mode)) {
std::cout << "target size: " << info.st_size << "\n";
}
}
要注意lstat自己的st_size对于符号链接表示的是链接路径字符串长度,而不是目标文件大小。如果业务需要目标文件大小,仍应使用stat。在目录遍历中同时使用两个函数,可以避免把符号链接误判为普通文件。
跨平台注意与现代替代方案
Windows环境下没有POSIX的stat,但MSVC提供了兼容函数_stat和_stat64,头文件为<sys/stat.h>或<sys/types.h>。其中_stat64的st_size是__int64类型,适合大文件。Linux和macOS还可以直接用stat64变体,但在64位系统上通常与stat等价。跨平台项目可以用条件编译封装一层接口。
如果项目使用C++17或更高版本,标准库提供了std::filesystem::file_size。它封装了不同平台的系统调用,返回std::uintmax_t,使用起来更简单。但在某些旧编译器或受限环境下可能不可用,而且它会抛出std::filesystem::filesystem_error,错误处理方式与errno不同。理解底层stat仍然有助于排查问题。
最后要提醒一个常见误区:不要依赖st_size来读取特殊文件。对管道、套接字或/proc下的虚拟文件,st_size可能为0或动态变化,但实际可以读取到数据。此时应当用循环读取直到EOF来确定字节数。判断普通文件是保证结果准确的前提。
C++获取文件大小stat函数struct stat修改时间:2026-09-24 07:08:30