簡體   English   中英

C預處理器如何處理循環依賴?

[英]How does the C preprocessor handle circular dependencies?

我想知道C預處理器如何處理循環依賴(#defines)。 這是我的計划:

#define ONE TWO 
#define TWO THREE
#define THREE ONE

int main()
{
    int ONE, TWO, THREE;
    ONE = 1;
    TWO = 2;
    THREE = 3;
    printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE,  TWO, THREE);
}

這是預處理器輸出。 我無法弄清楚為什么輸出是這樣的。 我想知道預處理器在這種情況下采取的各種步驟,以提供以下輸出。

# 1 "check_macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "check_macro.c"

int main()
{
 int ONE, TWO, THREE;
 ONE = 1;
 TWO = 2;
 THREE = 3;
 printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE, TWO, THREE);
}

我在linux 3.2.0-49-generic-pae上運行這個程序,並在gcc版本4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5)中編譯。

在擴展預處理器宏時,不會擴展該宏的名稱。 所以你的三個符號都被定義為自己:

ONE -> TWO -> THREE -> ONE (not expanded because expansion of ONE is in progress)
TWO -> THREE -> ONE -> TWO (        "                         TWO      "        )
THREE -> ONE -> TWO -> THREE (      "                         THREE    "        )

這種行為是由C標准的§6.10.3.4(C11草案中的部分編號)設定的,盡管據我所知,該部分的措辭和編號自C89以來沒有變化。 遇到宏名稱時,會將其替換為其定義(並處理###預處理程序運算符,以及類似函數的宏的參數)。 然后重新掃描結果以獲取更多宏(在文件的其余部分的上下文中):

2 /如果在替換列表的掃描期間找到要替換的宏的名稱(不包括源文件的其余預處理標記),則不會替換它。 此外,如果任何嵌套替換遇到要替換的宏的名稱,則不會替換它...

該條款接着說,任何因遞歸調用而未被替換的令牌都被有效地“凍結”:它永遠不會被替換:

...這些未替換的宏名稱預處理令牌不再可用於進一步替換,即使它們稍后(重新)檢查在其中否則將替換該宏名稱預處理令牌的上下文中。

最后一句所指的情況很少在實踐中出現,但這是我能想到的最簡單的情況:

#define two one,two
#define a(x) b(x)
#define b(x,y) x,y
a(two)

結果是one, two two被擴展到one,two更換期間a ,和擴展two被標記為完全展開。 隨后,擴展b(one,two) 這不再是替換two的上下文,但是b的第二個參數這two已被凍結,因此它不再被擴展。

您的問題由出版物ISO / IEC 9899:TC2第6.10.3.4節“重新掃描和進一步更換”,第2段回答,我在此引用為方便起見; 將來, 如果您對規范有疑問請考慮閱讀具體說明

如果在替換列表的掃描期間找到要替換的宏的名稱(不包括源文件的其余預處理標記),則不會替換它。 此外,如果任何嵌套替換遇到要替換的宏的名稱,則不會替換它。 這些未替換的宏名稱預處理令牌不再可用於進一步替換,即使它們稍后(重新)檢查在其中否則將替換該宏名稱預處理令牌的上下文中。

https://gcc.gnu.org/onlinedocs/cpp/Self-Referential-Macros.html#Self-Referential-Macros回答有關自引用宏的問題。

答案的關鍵在於,當預處理器找到自引用宏時,它根本不會擴展它們。

我懷疑,相同的邏輯用於防止循環定義的宏的擴展。 否則,預處理器將處於無限擴展狀態。

在您的示例中,您在定義同名變量之前執行宏處理,因此無論宏處理的結果如何,您始終打印1, 2, 3

以下是首先定義變量的示例:

#include <stdio.h>
int main()
{
    int A = 1, B = 2, C = 3;
#define A B
#define B C
//#define C A
    printf("%d\n", A);
    printf("%d\n", B);
    printf("%d\n", C);
}

這打印3 3 3 有點隱蔽,取消注釋#define CA改變行printf("%d\\n", B);的行為printf("%d\\n", B);

以下是riciEric Lippert的答案中描述的行為的一個很好的演示,即如果在擴展同一個宏時再次遇到宏名稱,則不會重新擴展宏名稱。

test.c內容:

#define ONE 1, TWO
#define TWO 2, THREE
#define THREE 3, ONE

int foo[] = {
  ONE,
  TWO,
  THREE
};

輸出gcc -E test.c (不包括初始# 1 ...行):

int foo[] = {
  1, 2, 3, ONE,
  2, 3, 1, TWO,
  3, 1, 2, THREE
};

(我會將此作為評論發布,但在評論中包含大量代碼塊有點尷尬,所以我將其作為社區Wiki答案。如果您認為將更好地包含在現有答案的一部分中,請隨意復制它並要求我刪除這個CW版本。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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