为什么我们不能使用预处理器来创建自定义分隔的字符串?

Ric*_*III 10 c macros gcc clang c-preprocessor

我正在玩C预处理器,当看起来如此简单的事情失败时:

#define STR_START "
#define STR_END "

int puts(const char *);

int main() {
    puts(STR_START hello world STR_END);
}
Run Code Online (Sandbox Code Playgroud)

当我用gcc编译它时(注意:与clang类似的错误),它失败了,出现这些错误:

$ gcc test.c
test.c:1:19: warning: missing terminating " character
test.c:2:17: warning: missing terminating " character
test.c: In function ‘main’:
test.c:7: error: missing terminating " character
test.c:7: error: ‘hello’ undeclared (first use in this function)
test.c:7: error: (Each undeclared identifier is reported only once
test.c:7: error: for each function it appears in.)
test.c:7: error: expected ‘)’ before ‘world’
test.c:7: error: missing terminating " character

哪种困惑我,所以我通过预处理器运行它:

$ gcc -E test.c
# 1 "test.c"
# 1 ""
# 1 ""
# 1 "test.c"
test.c:1:19: warning: missing terminating " character
test.c:2:17: warning: missing terminating " character

int puts(const char *);

int main() {
    puts(" hello world ");
}

尽管有警告,但产生完全有效的代码(在粗体文本中)!

如果,C中的宏只是文本替换,为什么我的初始示例会失败?这是编译器错误吗?如果没有,标准中的哪个部分有关于这种情况的信息?

注意:我不是在寻找如何使我的初始片段编译.我只是在寻找有关此方案失败原因的信息.

Joh*_*ode 10

问题是,即使代码扩展为" hello, world ",预处理器也不会将其识别为单个字符串文字标记; 相反,它被识别为(无效)序列令牌",hello,,,world,".

N1570:

6.4词汇元素
...
3 令牌是翻译阶段7和8中语言的最小词汇元素.令牌的类别是:关键词,标识符,常量,字符串文字和标点符号.预处理标记是翻译阶段3到6中语言的最小词汇元素.预处理标记的类别是:标题名称,标识符,预处理数字,字符常量,字符串文字,标点符号和单个非空白字符不要在词法上匹配其他预处理令牌类别.69) 如果一个'或一个"字符与最后一个类别匹配,则该行为未定义.预处理令牌可以用空格分隔; 这包括注释(稍后描述)或空白字符(空格,水平制表符,换行符,垂直制表符和换页符)或两者.如6.10中所述,在翻译阶段4期间的某些情况下,空白区域(或其缺失)不仅仅用于预处理标记分​​离.空格可能仅作为标题名称的一部分出现在预处理标记内,或出现在字符常量或字符串文字中的引号字符之间.
69)在翻译阶段4内部使用了一个附加类别的地方标记(见6.10.3.3); 它不会出现在源文件中.

请注意,在此定义下既不是'也不"是标点符号.


Mik*_*ley 7

预处理器分多个阶段运行.阶段3,标记化,在扩展之前发生,因此预处理器宏必须表示完整的标记.在你的情况下,STR_START并被STR_END标记化然后替换,这使得这些标记无效.