最可靠方式是通过getfileinformationbyhandle获取同卷内的dwvolumeserialnumber与nfileindexhigh/nfileindexlow组合进行比对,该组合在同卷内唯一标识文件(含硬链接),跨卷则直接由卷序列号判定不同。

Windows下用GetFileInformationByHandle比较文件ID
在Windows平台,最可靠的方式是获取两个路径对应的句柄,再通过GetFileInformationByHandle提取dwVolumeSerialNumber和nFileIndexHigh/nFileIndexLow组合——这组值在同卷内唯一标识一个文件(包括硬链接)。注意:不能只比对路径字符串或GetFileSize,因为符号链接、硬链接、挂载点都可能导致路径不同但指向同一实体。
实操要点:
- 必须用
CREATE_FILE_FLAG_BACKUP_SEMANTICS打开目录句柄(否则GetFileInformationByHandle对目录会失败) - 若任一路径不存在,
CreateFile返回INVALID_HANDLE_VALUE,需先用GetFileAttributes预检 - 跨卷(比如C:\和D:\)时,
dwVolumeSerialNumber不同,直接判定为不同文件,无需继续比索引 - 对符号链接,
CreateFile默认解析目标;如需保留链接本身,加FILE_FLAG_OPEN_REPARSE_POINT
Linux/macOS用stat()比st_dev与st_ino
POSIX系统靠stat()返回的st_dev(设备号)和st_ino(inode号)联合判断。只要两者完全相等,就说明是同一物理文件。注意:lstat()和stat()行为不同——前者不解析符号链接,后者会穿透到目标,选哪个取决于你是否想把符号链接与其目标视为“同一文件”。
常见陷阱:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
stat()对不存在路径返回-1并设errno=ENOENT,必须检查返回值,不能只看st_ino - NFS或某些网络文件系统可能有inode复用或设备号不稳定问题,此时
st_dev+st_ino可能失效 - 某些FUSE文件系统(如sshfs)不保证
st_ino全局唯一,需结合st_mtime+st_size辅助判断(但非绝对可靠)
跨平台方案:用std::filesystem::canonical()后字符串比较?
别这么做。std::filesystem::canonical()看似方便,但它在遇到符号链接、挂载点、大小写敏感/不敏感文件系统时行为不一致:Windows NTFS默认不区分大小写,但canonical()返回路径可能含大写驱动器名;Linux ext4区分大小写,而某些路径可能含URL编码或特殊字符。更严重的是,它无法处理硬链接——两个硬链接的canonical()结果必然不同,哪怕指向同一inode。
真正可行的跨平台路径归一化,只有两条路:
- 各平台调用原生API(Windows用
GetFileInformationByHandle,POSIX用stat),封装统一接口 - 如果只需处理普通文件且能接受一定误判率,可退而求其次:先
stat确认存在且是常规文件,再逐字节比对内容哈希(SHA256)——但这对大文件极慢,且无法区分硬链接和副本
容易被忽略的权限与访问边界问题
即使逻辑上两路径指向同一文件,程序也可能因权限不足拿不到足够信息:比如对目录无读权限时,stat()可能失败;Windows下若无SE_BACKUP_NAME特权,OpenFile对系统保护文件会拒绝访问。此时函数返回错误,而不是给出“否”的结论。
这意味着:
- 不能把
stat失败等价于“不是同一文件”,而应明确区分“无法判断”和“确定不同” - 若业务允许降级,可记录
errno或GetLastError()值,后续走内容比对或人工介入 - 容器或沙箱环境(如Docker、Flatpak)中,/proc/self/fd/下的符号链接可能指向宿主机路径,
stat结果与预期不符,需额外检查/proc/mounts或findmnt
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










