std::rename跨C盘与D盘失败是因为其底层不支持跨文件系统移动,仅能同卷重命名;跨卷需手动复制再删除,并注意二进制模式、覆盖选项、原子性临时文件及设备ID校验。

为什么 std::rename 在 C 盘到 D 盘时直接返回 -1
因为 std::rename 底层调用的是 POSIX rename(2) 或 Windows API MoveFileEx,而跨文件系统(如 C: 和 D: 通常是不同卷)时,标准 rename 不支持“移动”,只支持“重命名”。它无法原子地搬运数据,只能改路径名——这在同卷下可行,跨卷就失败,errno 通常为 EXDEV(Linux/macOS)或返回 false(Windows)。
跨卷移动必须手动复制 + 删除
没有捷径。得先完整复制文件内容,再删除源文件。关键点不是“怎么写循环读写”,而是怎么避免常见翻车:
-
std::ifstream和std::ofstream必须用std::ios::binary模式打开,否则 Windows 下换行符会被悄悄转换,文件变样 - 别用
std::filesystem::copy_file默认行为——它不覆盖已存在目标,默认抛std::filesystem::filesystem_error;得显式传std::filesystem::copy_options::overwrite_existing - 复制成功后,必须检查
std::filesystem::remove返回值;失败时源文件还在,但你已经删了句柄或误判成功,容易丢数据
示例片段:
namespace fs = std::filesystem;
fs::path src = "C:/temp/data.bin";
fs::path dst = "D:/backup/data.bin";
try {
fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
if (fs::remove(src)) {
// 成功
} else {
// remove 失败:权限不足、文件被占用等,src 仍存在
}
} catch (const fs::filesystem_error& e) {
// copy_file 失败:磁盘满、路径无效、权限拒绝等
}
大文件移动时的性能和中断风险
直接 copy_file 是阻塞同步操作,几 GB 的文件会卡住线程,且没进度、不可取消、断电就残缺。真实项目里要应对这些:
立即学习“C++免费学习笔记(深入)”;
- 用
std::ifstream::rdbuf()配合缓冲区(如 64KB)分块读写,比默认小缓冲快不少,也方便插桩进度/取消逻辑 - 移动前先用
fs::space检查目标盘剩余空间,避免复制到一半爆满 - 临时文件策略更安全:先写到
dst + ".tmp",复制完fs::rename重命名为目标(同卷 rename 是原子的),再删源;这样即使中断,也不会污染目标路径
Windows 下要注意驱动器字母和符号链接
看似是 C: → D:,但实际可能指向同一物理盘(比如 D: 是 C: 的 NTFS 符号链接),这时 rename 可能意外成功。别依赖盘符判断是否跨卷——应该用 GetVolumePathName + GetDiskFreeSpaceEx(Windows)或 statfs(POSIX)比对设备 ID。C++20 std::filesystem::status 不提供设备号,所以跨平台时得条件编译调系统 API。
这事容易被忽略:测试时在开发机上 C/D 是不同盘,上线后客户用的是挂载点或存储池,盘符相同但物理分离,或者相反——逻辑判断必须落到真实设备层面,不能只看路径字符串。



















