交叉文件#if和#endif - 它应该合法吗?

Hai*_*ang 2 c c++ c-preprocessor preprocessor-directive c11

根据C11标准,

表单的预处理指令

#include"q-char-sequence"换行符

导致由"分隔符"之间的指定序列标识的源文件的全部内容替换该指令.

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

#endif
Run Code Online (Sandbox Code Playgroud)

一个源文件test.c包含:

#if 1
#include "test.h"
Run Code Online (Sandbox Code Playgroud)

是否应该根据标准通过更换现有内容来通过预处理阶段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.
Run Code Online (Sandbox Code Playgroud)

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

Som*_*ude 5

如果您阅读例如此C++ #include引用,则所包含的文件首先通过转换阶段 1到4 运行,而第4阶段运行预处理器(递归地).

这意味着您包含的文件必须完整,#if并且#endif.

这也发生在C语言中.


在阅读C11规范(ISO/IEC 9899:2011 [2012])后,我认为发生的是:

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

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

当包含的头文件本身的处理涉及到第四阶段,并开始处理#endif指令,那么它不走在递归列入堆栈找到匹配的#if,预处理器只着眼于当前堆栈帧(当前文件).因此,您得到关于否的第一个错误#if.该标准实际上没有说明这一点.所有它说,基本上,是#if必须有匹配#endif.编译器没有通过包含堆栈来查找匹配#if似乎更像是一个实现细节.

无论如何,预处理器在阶段4中用步骤3结束其对头文件的处理,即

在此阶段结束时,将从源中删除所有预处理程序指令.

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