whi*_*ark 12 c language-lawyer
假设我有两个文件a.h:
#if 1
#include "b.h"
Run Code Online (Sandbox Code Playgroud)
并且b.h:
#endif
Run Code Online (Sandbox Code Playgroud)
gcc和clang的预处理器都拒绝a.h:
$ cpp -ansi -pedantic a.h >/dev/null
In file included from a.h:2:0:
b.h:1:2: error: #endif without #if
#endif
^
a.h:1:0: error: unterminated #if
#if 1
^
Run Code Online (Sandbox Code Playgroud)
但是,C标准(N1570 6.10.2.3)说:
表单的预处理指令
# include "q-char-sequence" new-line导致由
"分隔符之间的指定序列标识的源文件的全部内容替换该指令.
这似乎允许上面的结构.
gcc和clang在拒绝我的代码方面不合规吗?
Kei*_*son 13
C标准定义了8个翻译阶段.源文件按顺序(或以等效方式)由8个阶段中的每个阶段处理.
N1570第5.1.1.2节中定义的第4阶段是:
执行预处理指令,扩展宏调用,并
_Pragma执行一元运算符表达式.如果通过标记连接(6.10.3.3)生成与通用字符名称的语法匹配的字符序列,则行为未定义.甲#include预处理指令导致从第1阶段至第4阶段处理指定的头或源文件,递归.然后删除所有预处理指令.
这里的相关句子是:
甲
#include预处理指令导致从第1阶段至第4阶段处理指定的头或源文件,递归.
这意味着每个包含的源文件都是自己预处理的.这排除了#if在一个文件中和#endif在另一个文件中相应的文件.
(正如评论中提到的"野象",正如罗德里戈的回答所说,第6.10节中的语法也表示if-section以#if(#ifdef或#ifndef)行开头并以一行结尾#endif,只能作为一部分出现一个预处理文件.)
我认为编译器是正确的,或者至多标准是模糊的.
诀窍不是如何#include实现,而是按照预处理的顺序完成.
查看C99标准第6.10节中的语法规则:
preprocessing-file:
group[opt]
group:
group-part
group group-part
group-part:
if-section
control-line
text-line
# non-directive
if-section:
if-group elif-groups[opt] else-group[opt] endif-line
if-group:
# if constant-expression new-line group[opt]
...
control-line:
# include pp-tokens new-line
...
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,这些#include东西嵌套在里面group,并且group是内部的东西#if / #endif.
例如,在格式良好的文件中,例如:
#if 1
#include <a.h>
#endif
Run Code Online (Sandbox Code Playgroud)
这将解析为#if 1,加上a group,plus #endif.里面group有一个#include.
但在你的例子中:
#if 1
#include <a.h>
Run Code Online (Sandbox Code Playgroud)
该规则if-section不适用于此来源,因此group甚至不检查制作.
可能你可以争辩说标准是不明确的,因为它没有指定#include指令的替换何时发生,并且一致的实现可能会改变许多语法规则并替换#include之前失败的未找到#endif.但是,如果语法的副作用会修改您正在解析的文本,则无法避免这些含糊不清.C不是很好吗?