为什么宏扩展有时会添加一个 space?
Why does macro expansion add a space sometimes?
如果我这样做:
#define F a
F/
扩展为a/
godbolt
如果我这样做:
#define F /
F/
它扩展为 / /
,在 goodbolt
之间有一个 space
但是如果我将它转换成字符串并打印出来,它不会添加任何 spaces goodbolt:
#include <stdio.h>
#define STR_IMPL(x) #x
#define STR(x) STR_IMPL(x)
#define F /
int main() {
puts(STR(F/));
}
为什么中间有一个 space,但只是在某些时候?在其他情况下是否允许添加更多的space?
宏在令牌级别工作。以字符串形式打印结果时添加空格以消除歧义。
F/
是两个标记,F
和 /
。宏展开后,还是需要两个token,/
和/
。但是如果你把它们并排打印,它就会变成 //
,这只是一个标记(注释)。所以中间需要有一个space。
长话短说:是的,宏扩展可以添加 spaces,因为 spaces 在宏操作的级别上无关紧要。
语言规范是这样的,高层次的:
- 将输入分解为一系列标记。
- 运行 该标记序列的预处理器(产生另一个标记序列)
- 编译生成的标记序列。
步骤之间没有任何中间纯文本表示的规范。您在“预处理器输出”中看到的完全是供应商的心血来潮,只要整个翻译过程的最终结果符合规定即可。
用#define F /
和STR(F/)
的例子分析:
首先请注意 #define F /
后跟换行符意味着 F
被两个标记 w /
替换(我正在使用w 表示白色space 令牌)。
标记化后 STR(F/)
的预处理标记序列为:
STR
、(
、F
、/
、)
第一轮宏替换后的记号是:
- w,
STR_IMPL
, (
, w, /
, /
, )
第二轮结束后,令牌为:
- w,
"//"
.
#
预处理运算符的定义包括删除前导白色space。所以 token 序列 w, /
, /
变成 "//"
, 而不是 " //"
.
F/
或两个斜杠之间从来没有任何 space。
如果我这样做:
#define F a
F/
扩展为a/
godbolt
如果我这样做:
#define F /
F/
它扩展为 / /
,在 goodbolt
但是如果我将它转换成字符串并打印出来,它不会添加任何 spaces goodbolt:
#include <stdio.h>
#define STR_IMPL(x) #x
#define STR(x) STR_IMPL(x)
#define F /
int main() {
puts(STR(F/));
}
为什么中间有一个 space,但只是在某些时候?在其他情况下是否允许添加更多的space?
宏在令牌级别工作。以字符串形式打印结果时添加空格以消除歧义。
F/
是两个标记,F
和 /
。宏展开后,还是需要两个token,/
和/
。但是如果你把它们并排打印,它就会变成 //
,这只是一个标记(注释)。所以中间需要有一个space。
长话短说:是的,宏扩展可以添加 spaces,因为 spaces 在宏操作的级别上无关紧要。
语言规范是这样的,高层次的:
- 将输入分解为一系列标记。
- 运行 该标记序列的预处理器(产生另一个标记序列)
- 编译生成的标记序列。
步骤之间没有任何中间纯文本表示的规范。您在“预处理器输出”中看到的完全是供应商的心血来潮,只要整个翻译过程的最终结果符合规定即可。
用#define F /
和STR(F/)
的例子分析:
首先请注意 #define F /
后跟换行符意味着 F
被两个标记 w /
替换(我正在使用w 表示白色space 令牌)。
标记化后 STR(F/)
的预处理标记序列为:
STR
、(
、F
、/
、)
第一轮宏替换后的记号是:
- w,
STR_IMPL
,(
, w,/
,/
,)
第二轮结束后,令牌为:
- w,
"//"
.
#
预处理运算符的定义包括删除前导白色space。所以 token 序列 w, /
, /
变成 "//"
, 而不是 " //"
.
F/
或两个斜杠之间从来没有任何 space。