当前位置: C语言 -- 基础 -- 预处理指令

预处理指令(十)

六、行控制

处理源文件到当前标记时当前源代码行的行号(line number)比在编译阶段步骤1中读取或者引入的换行符数量多一。C语言中行号的计算从1开始,基于换行符的数量,行号 = 换行符数量 + 1

int main(void)	//行号为1,换行符数为0。
{		//行号为2,换行符数为1。
    return 0;	//行号为3,换行符数为2。
}		//行号为4,换行符数为3。

如果一个预处理标记(特别是__LINE__。)跨越两个或者多个物理行,ISO标准未指定哪个行号与该预处理标记关联。如果一个预处理指令跨越两个或者多个物理行,ISO标准未指定哪个行号与该预处理指令关联。如果一个宏调用跨越两个或者多个物理行,ISO标准未指定哪个行号与该宏调用关联。

ISO标准建议:与预处理标记相关的行号应为该预处理标记首个字符所在行的行号。与预处理指令相关的行号应为首个#标记所在行的行号。与宏调用相关的行号应为调用语句中宏名称首个字符所在行的行号。


预处理标记的行号与上下文无关(特别是作为宏实参或者在预处理指令中。)。宏体中__LINE__的行号是宏调用所在行的行号;而不是宏定义所在行的行号。

#define F(x) __LINE__ + (x)

printf("%d\n", __LINE__);	//输出8。
printf("%d\n", F(100));	//输出109。

#line预处理指令用于编译时控制编译器报告的行号和文件名;#line预处理指令具有以下三种语法格式:

第一种格式:

#line digit-sequence new-line

其中line是指令名;digit-sequene是数字序列;new-line是换行符。

数字序列解释为十进制整数,并忽略其中可能的数字分隔符;数字序列表示的整数不能为0,也不能大于2147483647。该格式#line预处理指令使实现表现得像后续源代码行序列开始于数字序列指定行号的源代码行。

printf("%d\n", __LINE__); //输出6, 即行号为6。
#line 100
printf("%d\n", __LINE__); //输出100, 即行号为100。

第二种格式:

#line digit-sequence "s-char-sequenceopt" new-line

其中line是指令名;digit-sequene是数字序列;new-line是换行符。

数字序列的解释与第一种格式相同。该格式#line预处理指令以第一种格式类似的方式设置行号,并将源文件名更改为字符串字面量的内容。

printf("File name: %s\n", __FILE__);		//输出File name: test.c。
printf("Line number: %d\n", __LINE__);	//输出Line number: 7。
#line 100 "mytest.c"
printf("File name: %s\n", __FILE__);		//输出File name: mytest.c。
printf("Line number: %d\n", __LINE__);	//输出Line number: 101。

如果#line预处理指令中存在字符串字面量,字符串字面量应是字符字符串字面量(character string literal)。

#line 100 "mytest.c"		//合法,字符字符串字面量。

#line 200 u8"mytest1.c"	//非法,UTF-8字符串字面量。
#line 300 L"mytest2.c"		//非法,wchar_t字符串字面量。
#line 400 u"mytest3.c"		//非法,UTF-16字符串字面量。
#line 500 U"mytest4.c"		//非法,UTF-32字符串字面量。

第三种格式:

#line pp-tokens new-line

其中line是指令名;pp-tokens是预处理标记;new-line是换行符。

该格式#line预处理指令中line后的预处理标记会像普通文本一样处理,每个当前定义为宏名的标识符会被其替换列表替换;完成所有替换后生成的指令必须匹配前两种#line预处理指令中的一种,然后进行相应处理。

#define F(x, y) x # y

#line F(100, mytest.c)

由于换行符显式地包含在#line指令中作为预处理指令的一部分,因此在处理至首个预处理标记时读取的换行符数量可能因实现是否采用单遍预处理器(one-pass preprocessor)而有所不同。对于以下格式的#line预处理指令:

#line __LINE__ new-line

其后的行号可能存在两种不同值。

:单遍预处理器是指在一次线性扫描中完成所有预处理工作的实现。解析指令时单遍预处理器是字符导向的,在遇到指令结束的换行符前不会进行全面的词法分析。多遍预处理器(multi-pass preprocessor)是指将预处理明确划分为不同阶段(例如:词法分析、指令执行。)的实现。处理指令时多遍预处理器是记号导向的,指令的结束使用一个换行符来标记。ISO标准注意到这种实现差异的存在,因此在标准中允许这种实现上的差异,这种差异不会影响程序的最终语义,只影响预处理器内部的计数细节。)

printf("%d\n", __LINE__);	//输出7。
#line __LINE__
printf("%d\n", __LINE__);	//输出8。

:使用GCC编译器编译,版本号:GCC 13.4.0。)