windows下最可靠方式是用getmodulefilenamea/w获取exe绝对路径后取parent_path;std::filesystem::current_path()返回工作目录,与exe位置无关,不可用于定位同级资源文件。

Windows下用GetModuleFileName获取exe所在目录
程序运行时,资源文件通常和可执行文件放在同一级目录(比如 config.json 在 myapp.exe 旁边),最可靠的方式是先拿到当前exe的完整路径,再剥离文件名,得到目录。Windows API 提供了 GetModuleFileName,传入 NULL 就能获取当前模块(即主exe)的绝对路径:
#include <windows.h>
#include <string>
#include <filesystem>
std::string getExeDir() {
char path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
return std::filesystem::path(path).parent_path().string();
}
</filesystem></string></windows.h>
注意:GetModuleFileName 返回的是ANSI路径(GetModuleFileNameA),若项目启用了Unicode(默认VS项目),应改用 GetModuleFileNameW 并配合 std::wstring 或转UTF-8;否则中文路径会乱码。
macOS/Linux用_dladdr获取可执行文件路径
POSIX系统没有直接等价的API,但可通过 _dyld_get_image_name(macOS)或 _dladdr(Linux)反查当前可执行映像路径。更通用、跨平台的做法是依赖 _dladdr(glibc提供),它需要链接 -ldl:
#include <dlfcn.h>
#include <libgen.h>
#include <unistd.h>
#include <string>
std::string getExeDir() {
Dl_info info;
if (dladdr(reinterpret_cast<void>(&getExeDir), &info) && info.dli_fname) {
char real_path[PATH_MAX];
if (realpath(info.dli_fname, real_path)) {
return std::string(dirname(real_path));
}
}
return {};
}
</void></string></unistd.h></libgen.h></dlfcn.h>
常见坑:dladdr 的第一个参数必须是**当前进程加载的某个符号地址**(不能是栈变量或字面量),用函数地址最稳妥;dirname 会修改传入的字符串,所以必须用副本;如果程序被 symlink 启动,realpath 能帮你解析到真实路径,避免资源找不到。
资源路径拼接时别硬编码斜杠
拿到exe目录后,拼资源路径时别写死 "./res/icon.png" 或 "res\icon.png" —— 这在跨平台场景下必然出错。统一用 std::filesystem::path 拼接:
- ✅ 正确:
(std::filesystem::path(getExeDir()) / "res" / "icon.png").string() - ❌ 错误:
getExeDir() + "/res/icon.png"(Linux/macOS可用,Windows可能混用和/导致路径失效) - ⚠️ 注意:
std::filesystem在C++17才正式标准化,编译需开-std=c++17,MSVC默认支持,GCC/Clang需显式指定
调试期 vs 发布期路径行为不一致
IDE里直接运行(如VS的F5、CLion的Run)时,工作目录通常是项目根目录或输出目录,不是exe所在目录——这时用 getcwd() 拿到的路径和 GetModuleFileName 完全不同。资源文件若按“相对exe”组织,就一定不能依赖当前工作目录。
验证方法:在代码开头加一行打印:
std::cout <p>你会发现两者经常不一致。真正上线打包后,用户双击exe启动,<code>getExeDir()</code> 才是唯一可信的基准点。</p>
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











