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

时间:2013-05-28 18:22:25

标签: c gcc macros clang c-preprocessor

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

#define STR_START "
#define STR_END "

int puts(const char *);

int main() {
    puts(STR_START hello world STR_END);
}

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

<子> 注意:我正在寻找如何使我的初始代码段编译。我只是在寻找有关此方案失败原因的信息。

3 个答案:

答案 0 :(得分:10)

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

N1570

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

请注意,'"都不是此定义下的标点符号。

答案 1 :(得分:6)

预处理器在multiple phases中运行。阶段3,标记化,在扩展之前发生,因此预处理器宏必须表示完整的标记。在您的情况下,STR_STARTSTR_END会被标记化然后替换,这会使这些令牌无效。

答案 2 :(得分:0)

下面

#define STR_START "

编译器需要字符串文字。字符串文字必须以结束引号结束。这就是编译器抱怨缺少终止"字符的原因。

宏扩展编译器再次抱怨,因为无效令牌。


例如,MSVC编译器抱怨换句话说:

error C2001: newline in constant

在扩张后,它抱怨缺少引号。