std::filesystem::current_path() 返回工作目录而非程序路径;正确方式需按系统调用:linux 读 /proc/self/exe,macos 用 _nsgetexecutablepath(),windows 用 getmodulefilenamea()。

用 std::filesystem::current_path() 获取的是工作目录,不是程序所在目录
很多人一上来就调用 std::filesystem::current_path(),结果发现返回的是终端启动时的路径,比如你在 /home/user 下执行 ./build/myapp,它就返回 /home/user —— 这不是你程序二进制文件所在的 ./build。工作目录和程序路径是两回事,别混。
正确做法得从可执行文件自身入手,不同系统有不同 API:
- Linux/macOS:读取
/proc/self/exe(Linux)或_NSGetExecutablePath()(macOS) - Windows:用
GetModuleFileNameA(NULL, ...) - C++20
std::filesystem不提供“获取当前可执行路径”的标准函数,别指望它
Linux 下可靠读取可执行文件路径:用 readlink("/proc/self/exe", ...)
/proc/self/exe 是一个符号链接,指向当前进程的可执行文件。只要没被 chroot 或 unshare 隔离,它几乎总是可用的。关键是要分配足够缓冲区并处理截断。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
PATH_MAX分配缓冲区(#include <limits.h></limits.h>),别用固定小数组 - 检查
readlink()返回值是否等于PATH_MAX - 1,若是,说明可能被截断,需要重试(不过实践中极少发生) - 记得用
dirname()提取目录部分,别直接当路径用 - 不要假设路径一定是绝对路径——理论上可能有 symlink 嵌套,但实际中
/proc/self/exe总返回绝对路径
示例片段(不带错误处理):
char path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
if (len > 0) {
path[len] = '\0';
char* dir = dirname(path); // 注意:dirname 可能修改原 buffer
}
Windows 下必须用 GetModuleFileNameA(),且注意字符编码
Windows 没有类似 /proc/self/exe 的机制,GetModuleFileNameA(NULL, ...) 是唯一稳定方案。参数传 NULL 表示当前模块(即主可执行文件)。
容易踩的坑:
- 缓冲区大小必须传入字节数,不是字符数;
MAX_PATH是字符数,所以用sizeof(char) * MAX_PATH不必要,直接传MAX_PATH即可(因为A版本是 ANSI) - 强烈建议用
GetModuleFileNameW()+wcstombs()或直接用宽字符路径,避免 ANSI 代码页导致中文路径乱码 - 返回值为 0 表示失败,需检查
GetLastError();常见错误是缓冲区太小(ERROR_INSUFFICIENT_BUFFER),但MAX_PATH通常够用
简写示意:
char path[MAX_PATH];
DWORD len = GetModuleFileNameA(NULL, path, MAX_PATH);
if (len > 0 && len < MAX_PATH) {
char* dir = dirname(path);
}
跨平台封装时,别硬凑统一接口
有人试图写个 get_executable_dir() 函数,内部用 #ifdef 分支,但返回类型、内存管理、错误语义很难对齐。比如 Linux 的 readlink 成功时返回字节数,Windows 的 GetModuleFileName 成功时返回字符数,且后者不保证末尾有 \0(虽然文档说会写)。
更务实的做法:
- 每个平台单独实现,函数名带上平台标识,如
get_executable_dir_linux(),避免隐藏差异 - 如果必须统一,返回
std::string并在每个分支里做完整错误检查和截断处理,不要依赖 caller 清理 - 别忘了测试 symlink 场景:用户用
ln -s /real/path/app /usr/local/bin/app启动,你得返回/real/path,而不是/usr/local/bin——readlink和GetModuleFileName都返回目标路径,不是 link 路径,这点倒是安全的
最常被忽略的一点:程序可能被 chdir() 改变工作目录,也可能被 execve() 替换镜像,但只要没 fork+exec 新进程,/proc/self/exe 和 GetModuleFileName 依然有效。真正不可靠的,是任何基于当前工作目录推导程序路径的“启发式”方法。










