GetDiskFreeSpaceEx无法统计文件夹大小,需递归遍历并用AllocationSize(Windows)或st_blocks×512(Linux/macOS)累加物理占用,注意权限、符号链接、硬链接及大文件溢出。

Windows 下用 GetDiskFreeSpaceEx 无法直接统计文件夹大小
这个函数只返回卷的总空间、空闲空间等全局信息,和具体文件夹无关。想算一个文件夹里所有文件占了多少物理磁盘空间(不是逻辑大小),得遍历 + 获取每个文件的「在磁盘上实际占用字节数」。
关键点在于:GetFileSizeEx 返回的是逻辑大小(比如 1KB 文件),而物理占用要看文件在磁盘上的簇(allocation unit)对齐情况——哪怕只有 1 字节,也可能占满整个簇(常见 4KB)。Windows 提供了 GetCompressedFileSizeEx?不,那是错觉;真正该用的是 GetFileInformationByHandleEx 配合 FILE_STANDARD_INFO 或更准的 FILE_INTERNAL_INFORMATION?也不对——最稳的方式是用 GetFileInformationByHandleEx + FILE_STANDARD_INFO 的 AllocationSize 字段,它表示“已为该文件分配的字节数”,即物理占用。
-
AllocationSize是四舍五入到簇大小的值,反映真实磁盘消耗 - 对稀疏文件、压缩/加密 NTFS 文件,
AllocationSize依然有效(不像GetCompressedFileSize可能返回压缩后大小) - 必须用句柄打开文件(
CreateFile),不能只靠路径调用 - 需要递归遍历子目录,跳过
.和..,且注意符号链接循环(可用dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT判断)
Linux/macOS 下用 stat.st_blocks * 512 是标准解法
POSIX 的 stat() 结构体里 st_blocks 字段单位是「512 字节块」,不管文件系统块大小是多少,它都按这个单位报已分配块数。乘以 512 就是物理占用字节数(st_blocks * 512)。
注意别误用 st_size(逻辑大小)或 st_blksize(推荐 I/O 块大小,和占用无关):
-
st_blocks对硬链接只计一次(合理,物理空间不重复) - 遇到挂载点(mount point)默认会跨过去,如需限制在单个文件系统,检查
stat.st_dev是否和根目录一致 - macOS 上
st_blocks行为和 Linux 一致,但某些旧版本 HFS+ 可能不准,APFS 没问题 - 用
ftw()或nftw()时注意回调函数里要自己调stat(),因为nftw默认传的是struct stat *,但字段是否填充取决于 flag(如FTW_PHYS避免跟随符号链接)
跨平台 C++ 实现必须区分路径遍历与属性获取逻辑
C++17 的 std::filesystem 能统一遍历(recursive_directory_iterator),但获取物理占用仍需平台 API:Windows 用 GetFileInformationByHandleEx,其他系统用 stat。不能依赖 file_size()——它返回 st_size,不是物理空间。
典型错误写法:accumulate += file_size(p) —— 这算的是所有文件逻辑大小之和,远小于真实磁盘占用(尤其小文件多时)。
- Windows 路径要用
CreateFile打开,注意加FILE_FLAG_BACKUP_SEMANTICS才能打开目录句柄(虽然这里只处理文件) - Linux/macOS 下
stat()失败常见原因是权限不足(如进入/proc子目录),需捕获errno == EACCES并跳过 - 大文件夹遍历时,避免频繁
std::filesystem::status()(它可能触发额外 syscall),优先用 iterator 自带的symlink_status()缓存结果 - 累加变量必须用
uint64_t,32 位size_t在 >4GB 场景下溢出
容易被忽略的边界:稀疏文件、NTFS 压缩、硬链接与权限
物理空间统计不是简单相加,底层行为会影响结果。例如:
- NTFS 压缩文件:
AllocationSize已体现压缩后实际占用,无需额外处理;但若用GetCompressedFileSize反而可能低估(它返回压缩流长度,不包含元数据开销) - 稀疏文件:
AllocationSize只计已分配区域,空洞部分不占空间,这是正确行为 - 硬链接:多个路径指向同一 inode,
stat.st_nlink > 1时若不做去重,会重复计算物理空间——但通常不应去重,因为每个硬链接都是合法入口,用户删掉其中一个并不释放空间;所以「按路径统计」是合理策略 - 权限:Windows 上没有读权限的文件,
CreateFile会失败(ERROR_ACCESS_DENIED),应跳过而非中止;Linux 下stat()同样可能失败,需检查errno
最麻烦的是符号链接循环和挂载点穿透——除非明确需求,否则默认不跟随符号链接、不跨文件系统,比强行处理更安全。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











