包含文件中的#endif可用于关闭包含文件中的#if吗?

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,只能作为一部分出现一个预处理文件.)


rod*_*igo 8

我认为编译器是正确的,或者至多标准是模糊的.

诀窍不是如何#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不是很好吗?