我正在使用 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 中的宏只是文本替换,为什么我的初始示例会失败?这是编译器错误吗?如果没有,标准中的哪个部分有关于这种情况的信息?
<子> 注意:我不正在寻找如何使我的初始代码段编译。我只是在寻找有关此方案失败原因的信息。 子>
答案 0 :(得分:10)
问题在于,即使代码扩展为" hello, world "
,预处理器也不会将其识别为单个字符串文字标记;相反,它被识别为令牌"
,hello
,,
,world
,"
的(无效)序列。
6.4词汇元素
...
3 标记是翻译阶段7和8中语言的最小词汇元素 标记的类别是:关键字,标识符,常量,字符串文字和标点符号。 预处理标记是翻译中语言的最小词汇元素 阶段3到6.预处理令牌的类别是:标题名称, 标识符,预处理数字,字符常量,字符串文字,标点符号和 单个非空白字符,与其他预处理没有词法匹配 令牌类别。 69) 如果'
或"
字符与最后一个类别匹配,则行为为 理解过程科幻奈德的。预处理令牌可以用空格分隔;这包括 注释(稍后描述)或空白字符(空格,水平制表符,换行符, 垂直制表符和换页符)或两者。如6.10所述,在某些情况下 在翻译阶段4期间,白色空间(或其缺失)用作多于 预处理令牌分离。空白区域可能出现在预处理令牌中 仅作为标题名称的一部分或在字符常量中的引号字符之间 或字符串文字。
69)在翻译阶段4内部使用了另一类别,地方标记(见6.10.3.3);这不可以 发生在源文件中。
请注意,'
和"
都不是此定义下的标点符号。
答案 1 :(得分:6)
预处理器在multiple phases中运行。阶段3,标记化,在扩展之前发生,因此预处理器宏必须表示完整的标记。在您的情况下,STR_START
和STR_END
会被标记化然后替换,这会使这些令牌无效。
答案 2 :(得分:0)
下面
#define STR_START "
编译器需要字符串文字。字符串文字必须以结束引号结束。这就是编译器抱怨缺少终止"
字符的原因。
宏扩展编译器再次抱怨,因为无效令牌。
例如,MSVC编译器抱怨换句话说:
error C2001: newline in constant
在扩张后,它抱怨缺少引号。