最可靠方式是Windows用GetModuleFileNameA、Linux/macOS用readlink("/proc/self/exe");argv[0]不可靠,std::filesystem::canonical也不适用。

Windows 下用 GetModuleFileNameA 获取可执行文件路径
Windows 上最可靠的方式是调用系统 API GetModuleFileNameA,它能准确返回当前进程主模块(即 .exe)的完整路径,不受工作目录影响。
常见错误是误用 GetCurrentDirectoryA —— 它返回的是当前工作目录,不是程序所在目录,双击桌面快捷方式启动时经常错得离谱。
-
GetModuleFileNameA(NULL, buffer, sizeof(buffer))第一个参数传NULL表示获取当前进程主模块路径 - 缓冲区必须足够大(建议至少
MAX_PATH,即 260 字节),否则可能截断 - 返回值为实际写入长度,为 0 表示失败,需检查
GetLastError() - 路径含盘符和反斜杠(如
"C:\app\main.exe"),如只需目录,得手动截掉最后的文件名
char path[MAX_PATH];
if (GetModuleFileNameA(NULL, path, sizeof(path)) == 0) {
// 处理错误
}
// path 现在是 "D:\proj\demo.exe",可用 strrchr(path, '\') 找到最后一个反斜杠
Linux/macOS 下用 /proc/self/exe 符号链接
Linux 和 macOS(基于 Darwin)都支持读取 /proc/self/exe 这个符号链接,它指向当前进程的可执行文件。这是最通用、不依赖编译器扩展的方式。
容易踩的坑是直接 fopen("/proc/self/exe") —— 这是符号链接,得用 readlink 读取目标路径,否则打开失败或读到空内容。
立即学习“C++免费学习笔记(深入)”;
-
readlink("/proc/self/exe", buffer, sizeof(buffer)-1)返回实际路径长度,末尾需手动加' ' - 如果程序被
chroot或容器隔离,/proc/self/exe可能不可读,需 fallback 到其他方式(如argv[0]) - 返回路径可能是相对路径(如
"./myapp"),需结合getcwd解析成绝对路径 - macOS 不提供
/proc,但_NSGetExecutablePath可替代,不过仅限于主程序,不适用于 dylib
char path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", path, sizeof(path)-1);
if (len != -1) {
path[len] = ' ';
}
跨平台封装要注意 argv[0] 的不可靠性
很多人想用 argv[0] 简单获取路径,但它只保证“可执行文件名”,不保证是绝对路径,也不保证存在——shell 可能只传了命令名(如 "myapp"),甚至传了软链接路径或重命名后的名字。
它唯一靠谱的场景是:你明确控制了启动方式(比如自己写的 shell 脚本里用 $(realpath "$0") 预处理好再传给程序),否则不能作为路径来源。
-
argv[0]可能不含目录(如"app"),也可能含"../bin/app"这种相对路径 - 在 systemd 服务或某些容器环境中,
argv[0]可能被覆盖为"(systemd)"或空字符串 - 即使拼上
getcwd(),也只得到“启动时的工作目录 + argv[0]”,不是程序安装位置
C++17 std::filesystem::current_path() 和 std::filesystem::canonical() 不解决根本问题
std::filesystem::current_path() 就是 C 风格的 getcwd(),返回工作目录;而 std::filesystem::canonical(argv[0]) 在多数情况下会失败或返回错误路径,因为 argv[0] 本身就不含可靠信息。
别指望标准库自动帮你“猜出程序在哪”——C++ 标准没定义“程序路径”的概念,所有实现都依赖 OS 特性。强行用 canonical 只会让错误更隐蔽(比如静默返回 /tmp/xxx)。
-
std::filesystem::canonical()要求输入路径存在且可访问,argv[0]很多时候不满足 - 在 Windows 上,若
argv[0]是短文件名(如"PROGRA~1"),canonical可能解析失败 - 跨平台代码里混用
std::filesystem和系统 API 时,注意 Windows 路径分隔符仍是'\',别硬替换成'/'
GetModuleFileNameA / readlink("/proc/self/exe")),失败再 fallback 到 argv[0] + getcwd(),最后才考虑环境变量或配置文件。没人能绕过操作系统这层——你得知道你在哪个系统上跑,以及那个系统到底提供了什么。










