【问题标题】:Cross-file #if and #endif - should it be legal?交叉文件#if 和#endif - 它应该是合法的吗?
【发布时间】:2016-10-14 03:15:56
【问题描述】:

根据C11标准,

表单的预处理指令

# 包含“q-char-sequence”换行符

用“分隔符之间的指定序列标识的源文件的全部内容替换该指令。

所以如果我有一个头文件test.h 包含:

#endif

还有一个源文件test.c 包含:

#if 1
#include "test.h"

难道不应该按照标准把test.h的内容替换原地通过预处理阶段吗?

但我不能用 clang 做到这一点,它说:

In file included from test.c:2:
./test.h:1:2: error: #endif without #if
#endif
 ^
test.c:1:2: error: unterminated conditional directive
#if 1
 ^
2 errors generated.

那么标准规定的行为是什么?

【问题讨论】:

    标签: c++ c c-preprocessor c11 preprocessor-directive


    【解决方案1】:

    如果您阅读例如this C++ #include reference 包含的文件首先通过translation phases 运行一到四,phase four 正在运行预处理器(递归)。

    这意味着您包含的文件必须是完整的 #if#endif

    这也发生在 C 中。


    在阅读了 C11 规范 (ISO/IEC 9899:2011 [2012]) 之后,我认为会发生这样的事情:

    编译器处于预处理器阶段 (phase 4) 并评估 #if 1 预处理器指令。条件评估为真,因此它进入条件内的块。有看到#include "test.h" 指令。

    当编译器处理包含指令时,它会暂时停止处理当前文件,以处理包含的文件。在继续处理当前源文件之前,对包含文件的处理会经过编译阶段 1 到 4(含)。

    当被包含的头文件本身的处理进入第4阶段并开始处理#endif指令时,它不会在递归包含堆栈中向上找到匹配的@ 987654331@,预处理器只查看当前堆栈帧(当前文件)。因此,您会收到关于 no #if 的第一个错误。该标准实际上并没有说明这一点。基本上,它所说的只是#if 必须有一个匹配的#endif。编译器没有进入包含堆栈来查找匹配的 #if 似乎更多的是实现细节。

    不管怎样,预处理器在第4阶段以第3步结束对头文件的处理,也就是

    在此阶段结束时,所有预处理器指令都将从源中删除。

    因此,当控件返回到源文件的预处理时,它实际包含的文件不包含任何预处理指令。基本上,包含的只是一个空文件。这导致了第二个错误,即#if 没有#endif,因为确实没有。

    【讨论】:

    • 这个答案没有任何解释。所有预处理都发生在同一阶段,因此这无助于理解发生了什么。请在下面查看我的答案。
    【解决方案2】:

    clang 是正确的:#if/#endif 在不同文件中的这种拆分不符合要求。这是因为预处理器首先扫描文件的其余部分以找到相应的#endif,然后才解析存在于保留的条件部分中的其他预处理器指令。

    这是 C 标准的第 6.10.1 p6 节。

    【讨论】:

    • 虽然我的回答并不完全正确,但这也不是。如果编译器会扫描源文件以找到匹配的#elif/#else/#endif,那么它根本不会包含"test.h" 头文件,唯一的错误将是关于未终止的#if .该标准(我已阅读 C11(2012 版))没有说明编译器应如何将预处理器指令 #if#endif 匹配。您提到的扫描是标准中遗漏的实施细节。
    猜你喜欢
    • 2017-02-20
    • 2010-10-08
    • 2019-06-12
    • 2017-11-17
    • 2021-06-24
    • 2013-03-11
    • 2023-03-30
    • 1970-01-01
    • 2011-03-28
    相关资源
    最近更新 更多