windows下用getmodulefilename(null, path, max_path)获取exe完整路径,再调用pathremovefilespec截去文件名;linux/macos分别读/proc/self/exe或调用_nsgetexecutablepath;跨平台应避免依赖argv[0],推荐使用std::filesystem::path安全拼接路径。

Windows下用GetModuleFileName获取exe所在目录
在Windows平台,GetModuleFileName是最直接可靠的方式——它返回当前模块(通常是主exe)的完整路径,包括驱动器和文件名。关键在于传入NULL作为第一个参数,表示获取当前进程的模块路径。
常见错误是只取到exe文件名而没截掉文件名部分,导致后续拼接资源路径失败。正确做法是调用PathRemoveFileSpec(需#include <shlwapi.h></shlwapi.h>)或手动找最后一个'\'位置。
- 必须链接
shlwapi.lib才能用PathRemoveFileSpec - 如果程序被快捷方式启动,
GetModuleFileName仍返回真实exe路径,不是快捷方式所在路径 - 返回路径是宽字符(
wchar_t*),若用ANSI版本需注意编码兼容性
#include <windows.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
<p>std::wstring get_exe_path() {
wchar_t path[MAX_PATH];
GetModuleFileName(nullptr, path, MAX_PATH);
PathRemoveFileSpec(path);
return std::wstring(path);
}</p></shlwapi.h></windows.h>
Linux/macOS用/proc/self/exe读符号链接
Linux和macOS没有等价的API,但可通过读取/proc/self/exe这个符号链接获得可执行文件的绝对路径。注意:该路径指向的是被加载的二进制文件,不是当前工作目录,也不是启动时的cwd。
容易踩的坑是直接readlink后不处理路径中的软链跳转——比如程序通过硬链接或软链接启动时,/proc/self/exe可能指向原始文件,也可能指向链接目标,取决于系统和调用方式。
-
readlink返回的路径不保证以/结尾,拼接子路径时要手动加/ - macOS没有
/proc,得用_NSGetExecutablePath(返回相对路径,需补全为绝对路径) - 某些容器环境(如Docker)中
/proc/self/exe可能不可读,需fallback到argv[0]并做realpath
#include <unistd.h>
#include <limits.h>
#include <libgen.h><p>std::string get_exe_dir() {
char path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", path, sizeof(path)-1);
if (len == -1) return "";
path[len] = ' ';
char* dir = dirname(path);
return std::string(dir);
}</p></libgen.h></limits.h></unistd.h>
跨平台封装要注意argv[0]的局限性
有人试图用main函数的argv[0]推导路径,但这是最不可靠的方式:argv[0]可能是相对路径、命令名(无路径)、甚至空字符串;shell重定向或execve调用时还能被任意篡改。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
仅当确定程序总以绝对路径启动,且不介意工作目录变更影响时,才考虑realpath(argv[0], nullptr)再dirname。但即便如此,在Windows下argv[0]不含扩展名的情况也常导致realpath失败。
- POSIX规定
argv[0]“通常”是程序名,但“通常”不等于“总是” - Windows下
GetModuleFileName比argv[0]稳定得多,优先选前者 - 跨平台库(如Boost.Filesystem)内部其实也是分平台调用上述原生方法,没走
argv[0]
获取路径后拼接资源文件要防双重斜杠
拿到目录路径后,拼接配置文件或资源路径时,最容易出错的是手动拼字符串导致//出现——比如get_exe_dir()返回/home/user/app,再加"/config.json"变成/home/user/app//config.json,多数系统能容忍,但某些嵌入式或严格解析器会报错。
更稳妥的做法是用std::filesystem::path(C++17)做拼接,它自动归一化分隔符和冗余斜杠;若不能用C++17,则手写一个安全拼接函数,检查末尾是否已有/或\。
-
std::filesystem::path在Windows下识别\,Linux下识别/,无需条件编译 - 即使路径含中文或空格,
std::filesystem::path也能正确处理,比字符串拼接安全 - 旧项目若禁用C++17,建议至少封装一次
strcat逻辑,避免到处重复判断斜杠
实际项目里,路径获取本身不难,难的是后续所有依赖它的操作——比如加载DLL、读取配置、写日志。一旦路径错了,错误往往延迟暴露,排查成本远高于写对那几行代码。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










