#line
是 C/C++ 预处理器的指令之一,主要用于修改编译器在报告错误和调试时使用的行号和文件名。它在以下场景特别有用:
- 代码生成工具(如
bison
、flex
、yacc
等)需要让错误信息指向原始输入文件,而不是生成的中间代码文件。 - 调试宏展开代码,使得错误信息能指向宏定义的位置,而不是宏调用的位置。
- 手动调整编译器错误信息,使其指向正确的源码位置(较少使用)。
1. #line
的基本语法
#line
指令有三种形式:
(1) #line linenum
#line 42
- 作用:从当前行开始,编译器会认为接下来的代码行号从
linenum
(本例是42
)开始递增。 - 示例:
#line 100 int x = y + z; // 如果这行有错误,编译器会报告它在 "行 100"
(2) #line linenum "filename"
#line 42 "original_source.c"
- 作用:
- 设置当前行号为
linenum
(本例是42
)。 - 设置当前文件名为
"filename"
(本例是"original_source.c"
)。 - 后续的错误信息会使用这个文件名和行号。
- 设置当前行号为
- 示例:
#line 100 "real_code.c" int a = b * c; // 如果这行出错,编译器会报告 "real_code.c:100: error: ..."
(3) #line anything_else
#define LINE_NUM 200
#define FILE_NAME "my_file.c"
#line LINE_NUM FILE_NAME
- 作用:
- 先对
anything_else
进行宏展开,最终必须匹配前两种形式之一。 - 适用于动态调整行号和文件名的情况。
- 先对
2. #line
如何影响 __LINE__
和 __FILE__
#line
会修改以下两个预定义宏的值:
__LINE__
:当前行号(受#line
影响)。__FILE__
:当前文件名(受#line
影响)。
示例:
#include <stdio.h>
int main() {
printf("Current file: %s, line: %d\n", __FILE__, __LINE__); // 输出原始位置
#line 100 "fake_file.c"
printf("Now file: %s, line: %d\n", __FILE__, __LINE__); // 输出 fake_file.c:101
return 0;
}
输出:
Current file: test.c, line: 4
Now file: fake_file.c, line: 101
注意:
#line 100
后,下一行的__LINE__
是101
(因为行号会自动递增)。
3. 典型应用场景
(1) 代码生成工具(Bison / Yacc / Flex)
这些工具会生成 .c
文件,但编译错误时,我们希望错误指向原始输入文件(如 .y
或 .l
文件),而不是生成的中间代码。
示例(Bison 生成的代码片段):
#line 1 "parser.y"
/* 这部分代码在 parser.y 的第 1 行 */
int parse() {
... }
这样,如果生成的代码有语法错误,编译器会报告 parser.y:XX
而不是 y.tab.c:XX
。
(2) 调试宏展开的代码
如果宏展开后报错,默认情况下错误指向的是宏调用的位置,但有时我们需要知道宏定义的位置。
示例:
#define CHECK(x) if (!(x)) {
printf("Error at line %d\n", __LINE__); }
#line 100 "macro_defs.h"
CHECK(ptr != NULL); // 如果出错,我们希望错误指向宏定义的位置,而不是调用位置
(3) 手动调整错误信息(较少使用)
#line 42
int x = y + z; // 如果这行出错,编译器会说 "line 42",而不是实际行号
4. 注意事项
-
#line
不会影响#include
的搜索路径- 它只修改
__FILE__
和__LINE__
,不会改变#include "file.h"
的查找方式。
- 它只修改
-
#line
通常在自动生成的代码中使用- 手动编写的代码一般不需要它,除非有特殊调试需求。
-
#line
可以嵌套- 后续的
#line
会覆盖之前的值。
- 后续的
-
#line 0
是特殊情况- 某些编译器(如 GCC)会将其视为“重置行号”,但标准未明确定义,建议避免使用。
5. 总结
用途 | 示例 |
---|---|
修改行号 | #line 100 |
修改文件名和行号 | #line 42 "source.c" |
动态调整(结合宏) | #line LINE "FILE" |
影响 __LINE__ 和 __FILE__ |
printf("%s:%d", __FILE__, __LINE__) |
适用场景:
- 代码生成工具(让错误指向原始文件)。
- 调试宏(让错误指向宏定义位置)。
- 特殊调试需求(手动调整错误信息)。
慎用:普通代码通常不需要手动使用 #line
,除非有特殊需求。