[英]Can #endif in an included file be used to close a #if in the including file?
假設我有兩個文件, ah
:
#if 1
#include "b.h"
和bh
:
#endif
gcc和clang的預處理器都拒絕ah
:
$ 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
^
但是,C標准( N1570 6.10.2.3 )說:
表單的預處理指令
# include "q-char-sequence" new-line
導致由
"
分隔符"
之間的指定序列標識的源文件的全部內容替換該指令。
這似乎允許上面的結構。
gcc和clang在拒絕我的代碼方面不合規嗎?
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
(或#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
...
如您所見, #include
包含在group
,而group
是#if / #endif
。
例如,在格式良好的文件中,例如:
#if 1
#include <a.h>
#endif
這將解析為#if 1
,加上一個group
,再加上#endif
。 內部group
有一個#include
。
但在你的例子中:
#if 1
#include <a.h>
規則if-section
不適用於此源,因此甚至不檢查group
產生。
可能你可以爭辯說標准是不明確的,因為它沒有指定何時發生#include
指令的替換,並且一致的實現可能會改變很多語法規則並在失敗之前替換#include
而沒有找到#endif
。 但是,如果語法的副作用會修改您正在解析的文本,則無法避免這些含糊不清。 C不是很好嗎?
將C預處理器視為一個非常簡單的編譯器,將C預處理器轉換為文件概念上執行幾個階段。
嚴格地說, C標准的 §5.1.1.2 (ISO / IEC 9899: 201x)中提到的與預處理有關的翻譯階段是階段3和階段4.階段3幾乎完全對應於詞法分析,而階段4是關於代碼生成。
從那張照片中似乎缺少句法分析(解析)。 實際上,C預處理器語法非常簡單,以至於真正的預處理器/編譯器與詞法分析一起執行它。
如果句法分析階段成功結束 - 即預處理翻譯單元中的所有語句根據預處理程序語法是合法的 - 可以進行代碼生成並執行所有預處理指令。
執行預處理指令意味着根據源語言的語義轉換源文件,然后從源文件中刪除指令。
每個預處理程序指令的語義在C標准的§6.10.1-6.10.9中指定。
回到您的示例程序,您提供的2個文件,即ah
和bh
,在概念上按如下方式處理。
詞法分析 - 每個單獨的預處理標記由左側的“{”和右側的“}”分隔。
ah
{#}{if} {1}
{#}{include} {"b.h"}
bh
{#}{endif}
這個階段沒有錯誤地執行,其結果,即預處理令牌的序列,被傳遞到后續階段:句法分析。
句法分析
下面給出了一個暫定的推導
preprocessing-file →
group →
group-part →
if-section →
if-group endif-line →
if-group #endif new-line →
…
並且很明顯,ah的內容不能從預處理語法中派生出來 - 事實上終止#endif
是缺失的 - 因此ah
語法上不正確。 這正是編譯器在寫入時告訴您的內容
a.h:1:0: error: unterminated #if
類似的事情發生在bh
; 向后推理, #endif
只能從規則中導出
if-section →
if-group elif-groups[opt] else-group[opt] endif-line
這意味着文件內容應該來自以下3組之一
# if constant-expression new-line group[opt]
# ifdef identifier new-line group[opt]
# ifndef identifier new-line group[opt]
既然情況並非如此,因為bh
不包含# if/# ifdef/# ifndef
而只包含單個#endif
行, bh
的內容在語法上也不正確,編譯器會以這種方式告訴你
In file included from a.h:2:0:
b.h:1:2: error: #endif without #if
代碼生成
當然,由於你的程序在詞法上是合理的,但在語法上是不正確的,所以這個階段永遠不會被執行。
#if / #ifdef / #ifndef
#elif
#else
#endif
必須在一個文件中匹配。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.