簡體   English   中英

包含文件中的#endif可用於關閉包含文件中的#if嗎?

[英]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預處理器轉換為文件概念上執行幾個階段。

  1. 詞法分析 - 將構成預處理翻譯單元的字符序列分組為預處理器語言中具有已識別含義( 標記 )的字符串。
  2. 句法分析 - 將預處理翻譯單元的標記分組為根據預處理語言語法構建的語法結構。
  3. 代碼生成 - 將構成預處理轉換單元的所有文件轉換為僅包含“純”C指令的單個文件。

嚴格地說, C標准的 §5.1.1.2 (ISO / IEC 9899: 201x)中提到的與預處理有關的翻譯階段是階段3和階段4.階段3幾乎完全對應於詞法​​分析,而階段4是關於代碼生成。

從那張照片中似乎缺少句法分析(解析)。 實際上,C預處理器語法非常簡單,以至於真正的預處理器/編譯器與詞法分析一起執行它。

如果句法分析階段成功結束 - 即預處理翻譯單元中的所有語句根據預處理程序語法是合法的 - 可以進行代碼生成並執行所有預處理指令。
執行預處理指令意味着根據源語言的語義轉換源文件,然后從源文件中刪除指令。
每個預處理程序指令的語義在C標准的§6.10.1-6.10.9中指定。

回到您的示例程序,您提供的2個文件,即ahbh ,在概念上按如下方式處理。

詞法分析 - 每個單獨的預處理標記由左側的“{”和右側的“}”分隔。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM