为什么 GCC 会删除预处理标记之间的空格?

Pav*_*kin 15 c gcc token language-lawyer c-preprocessor

示例代码:

#define X(x,y)  x y
#define STR_(x) #x
#define STR(x)  STR_(x)
STR(X(Y,Y))
Run Code Online (Sandbox Code Playgroud)

调用:

$ gcc t222.c -std=c11 -pedantic -Wall -Wextra -E -P
"Y Y"

$ gcc t222.c -std=c11 -pedantic -Wall -Wextra -E -P -D"Y()"
"YY"
Run Code Online (Sandbox Code Playgroud)

为什么 GCC 会删除预处理标记之间的空格?

例如, clang 不会:

$ clang t222.c -std=c11 -pedantic -Wall -Wextra -E -P -D"Y()"
"Y Y"
Run Code Online (Sandbox Code Playgroud)

UPD1。,不知何故,gcc 考虑了和之间的空格Y

$ gcc t222.c -std=c11 -pedantic -Wall -Wextra -E -P -D"Y()" -D"Z=STR(X(Y,Y))"
"YY"

$ gcc t222.c -std=c11 -pedantic -Wall -Wextra -E -P -D"Y()" -D"Z=STR(X(Y, Y))"
"Y Y"
Run Code Online (Sandbox Code Playgroud)

UPD2。这:

STR(X(Y,
Y))
Run Code Online (Sandbox Code Playgroud)

导致:

$ gcc t222.c -std=c11 -pedantic -Wall -Wextra -E -P -D"Y()"
"Y Y"
Run Code Online (Sandbox Code Playgroud)

然而,这个:

STR(X(Y
,Y))
Run Code Online (Sandbox Code Playgroud)

导致:

$ gcc t222.c -std=c11 -pedantic -Wall -Wextra -E -P -D"Y()"
"YY"
Run Code Online (Sandbox Code Playgroud)

UPD3。报告:https ://gcc.gnu.org/bugzilla/show_bug.cgi?id =104147。

Eri*_*hil 7

这是 GCC 中的一个错误。C 2018 6.10.3.2 规定了#操作员的行为。第 1 段说 \xe2\x80\x9c#类函数宏的替换列表中的每个预处理标记应后跟一个参数,作为替换列表中的下一个预处理标记。\xe2\x80\x9d 我们在 of 中看到这#x一点#define STR_(x) #x

\n

第 2 段说:

\n
\n

如果在替换列表中,参数前面紧跟着#预处理标记,则两者都将替换为单个字符串文字预处理标记,其中包含相应参数的预处理标记序列的拼写。参数\xe2\x80\x99s 预处理标记之间每次出现的空格都会成为字符串文字中的单个空格字符。构成参数的第一个预处理标记之前和最后一个预处理标记之后的空格被删除\xe2\x80\xa6

\n
\n

宏调用X(Y,Y)一定会产生标记YY,并且我们看到#define X(x,y) x y它们之间会有空格。

\n

根据 6.10.3 1,宏替换列表中的空格很重要,其中表示:

\n
\n

当且仅当两个替换列表中的预处理标记具有相同的编号、顺序、拼写和空格分隔时,两个替换列表才是相同的,其中所有空格分隔都被视为相同。

\n
\n

因此,在 中#define X(x,y) x y,替换列表不应被视为只是两个标记xy,而忽略空格。替换列表是x、空格和y

\n

此外,当宏被替换时,它被替换列表替换(因此包括空格),而不仅仅是替换列表中的标记,根据 6.10.3 10:

\n
\n

\xe2\x80\xa6 类似函数的宏名称的每个后续实例,后跟 a(作为下一个预处理标记,引入了由定义中的替换列表替换的预处理标记序列(宏的调用)\xe2\ x80\xa6 在构成类函数宏调用的预处理标记序列中,换行符被视为普通空白字符。

\n
\n