__file__ 和 __line__ 是标准预定义宏,分别展开为预处理器看到的文件路径和宏展开处行号;封装日志宏时需将二者置于宏体内部以确保行号准确,推荐用 do { ... } while(0) 包裹,并优先使用标准宏 __func__ 替代 __function__。

__FILE__ 和 __LINE__ 宏的基本用法
直接用 __FILE__ 和 __LINE__ 就能拿到当前编译位置的文件路径和行号,它们是 C++ 标准预定义宏,无需头文件、无运行时开销。
常见写法示例:
#include <iostream>
int main() {
std::cout << "File: " << __FILE__ << ", Line: " << __LINE__ << '
';
return 0;
}
但要注意:__FILE__ 展开的是**预处理器看到的路径**,可能含相对路径、符号链接残留,甚至完整绝对路径(取决于编译命令中怎么写的 -I 或源文件路径);__LINE__ 是宏展开那一刻所在行号,不是调用点——这点在宏嵌套时极易误判。
封装成日志宏时为什么总打错行号?
问题出在宏展开层级。如果写成这样:
立即学习“C++免费学习笔记(深入)”;
#define LOG(msg) std::cerr << __FILE__ << ":" << __LINE__ << " " << msg << ' '
那 __LINE__ 指的是宏定义所在的行,不是调用 LOG("xxx") 的那行。正确做法是用“立即展开”技巧:
- 把
__FILE__和__LINE__放进宏体内部,而非定义体中 - 避免中间宏层转发,否则行号会漂移
- 推荐写法:
#define LOG(msg) do { std::cerr << __FILE__ << ":" << __LINE__ << " " << (msg) << ' '; } while(0)
这样每次调用 LOG,__LINE__ 都取自调用语句所在行,才符合直觉。
跨平台下 __FILE__ 路径太长或含敏感信息怎么办?
__FILE__ 在 Windows 上常用反斜杠 ,Linux/macOS 用正斜杠 /,且可能暴露项目绝对路径(CI/CD 或交付二进制时有风险)。
安全又简洁的处理方式:
- 用
std::string_view(__FILE__).substr(...)截取最后几级目录(需 C++17) - 更稳妥:借助
__BASE_FILE__(GCC/Clang 支持),它只返回被编译的主源文件名,不含路径 - 若必须裁剪路径,建议用宏拼接提取文件名部分:
#define SHORT_FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\') ? strrchr(__FILE__, '\') + 1 : __FILE__),然后在日志中用SHORT_FILE
注意:strrchr 是运行时函数,不能用在 #if 条件编译里;且该写法依赖 C 运行库,在 freestanding 环境(如裸机)不可用。
__FUNCTION__ 和 __func__ 有什么区别?
__FUNCTION__ 是 GCC 扩展,__func__ 是 C99/C++11 标准宏,两者都展开为当前函数名字符串字面量(不是 const char* 类型,而是字符数组)。
关键差异:
-
__func__是标准、可移植,推荐优先使用 -
__FUNCTION__在某些旧编译器上可能包含签名(如void foo(int)),而__func__严格只返回未修饰函数名("foo") - 二者都不能在全局作用域或 lambda 内使用(lambda 没有函数名)
- 若想组合行号+函数+文件,典型日志宏形如:
#define DEBUG_LOG(msg) std::cerr << "[" << __FILE__ << ":" << __LINE__ << " in " << __func__ << "] " << (msg) << ' '
宏里不要对 __func__ 取地址或传给需要 const char* 的 API(比如 printf),它本质是 static const char __func__[] = "xxx";,隐式转换通常 OK,但某些模板推导会失败。











