__FILE__和__LINE__是C++标准预定义宏,编译时由编译器自动替换为当前源文件路径和行号,无运行时开销;常见用法如std::cout << __FILE__ << ":" << __LINE__;。

用 __FILE__ 和 __LINE__ 宏最直接
这两个是 C++ 标准预定义宏,编译时由编译器自动替换为当前源文件的绝对路径(或相对路径,取决于编译选项)和行号。它们不是函数调用,不产生运行时开销,也不依赖任何头文件。
常见写法:
std::cout << "File: " << __FILE__ << ", Line: " << __LINE__ << std::endl;
注意点:
-
__FILE__在不同编译器下路径格式可能不同(如 MSVC 默认用反斜杠,GCC/Clang 用正斜杠/),若需统一处理,建议用std::filesystem::path解析 -
__LINE__是整型字面量,可直接参与 constexpr 计算,比如做静态断言:static_assert(__LINE__ > 100, "too early"); - 宏展开发生在预处理阶段,所以它们反映的是宏被写入的位置,不是调用位置——这点在封装日志宏时特别关键
封装成日志宏时,__FILE__ 要截取文件名而非全路径
直接输出 __FILE__ 往往太长(比如 /home/user/project/src/main.cpp),日志里通常只关心 main.cpp。但不能在宏里用 std::string 处理,因为宏展开是纯文本替换,没有运行时能力。
立即学习“C++免费学习笔记(深入)”;
推荐做法是用另一个宏辅助截取:
#define CURRENT_FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 :
strrchr(__FILE__, '\') ? strrchr(__FILE__, '\') + 1 : __FILE__)
#define LOG_INFO() std::cout << "[" << CURRENT_FILE << ":" << __LINE__ << "] "
说明:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 这个
CURRENT_FILE是纯宏实现,兼容 C++11 及以上,不依赖 STL 运行时 - 它同时适配 Unix 和 Windows 路径分隔符,但注意:如果
__FILE__是相对路径(如src/util.h),且不含分隔符,就直接用原值 - 某些构建系统(如 Bazel)可能把
__FILE__设为短名(如util.h),此时该宏退化为恒等式,无副作用
想在函数里动态获取?别信运行时反射,C++ 没原生支持
C++ 标准至今(C++23)不提供类似 Python 的 inspect.stack() 或 Rust 的 file!() 运行时求值机制。所谓“动态获取当前行号”本质仍是宏——只是被包装得更隐蔽。
常见误区:
- 写一个普通函数
get_line()试图返回__LINE__:不行,__LINE__在函数定义处展开,不是调用处 - 用
std::source_location(C++20):它确实能获取调用点信息,但必须显式传参,且不是“自动”获取——你得在每个调用点写std::source_location::current() - 示例(C++20):
void log(const char* msg, const std::source_location& loc = std::source_location::current()) {
std::cout << loc.file_name() << ":" << loc.line() << " " << msg << std::endl;
}
// 调用时:log("hello"); // 自动捕获调用点
注意:std::source_location::current() 是编译器内置魔术,不是普通函数调用;它在模板实例化或宏中行为稳定,但老编译器(如 GCC 10 以下)不支持。
调试时打印行号,但发布版要关掉?用条件宏控制
日志宏带文件行号会增大二进制体积(字符串字面量增多),调试时有用,上线后可能想裁掉。靠 #ifdef DEBUG 最稳妥:
#ifdef DEBUG
#define DEBUG_LOG(msg) std::cout << "[" << __FILE__ << ":" << __LINE__ << "] " << msg << std::endl
#else
#define DEBUG_LOG(msg) do {} while(0)
#endif
要点:
- 不要用
if constexpr或运行时判断来“开关”宏,那无法真正消除字符串字面量 - 如果项目用 CMake,确保
DEBUG宏在 Debug 构建中定义:target_compile_definitions(myapp PRIVATE $:DEBUG>) - 注意宏名冲突:像
LOG这种通用名容易和第三方库(如 glog、spdlog)撞车,建议加项目前缀
实际用的时候,<strong>FILE</strong> 和 <strong>LINE</strong> 看似简单,但宏展开时机、路径分隔符差异、发布版裁剪这些细节,往往在跨平台或长期维护时才暴露出来。

















