這個宏會導致 GCC 崩潰嗎?讀完你就會有答案
本文的目標是向您介紹 C 語言宏的壯麗世界。
在 C 中,以 # 開頭的行由編譯器在編譯原始檔時解釋。這些稱為預處理器指令。宏就是其中之一。
小歷史點:
C 語言宏是隨第一個 C 語言標準引入的,稱為 ANSI C(或 C89),
於 1989 年由美國國家標準協會 (ANSI) 標準化。然而,在此標準化之前,宏已經成為 20 世紀 70 年代使用的經典 C(或 K&R C)語言的一部分
。 最初的 C 編譯器由 Dennis Ritchie 為 UNIX 作業系統開發,已經透過預處理器包含了基本形式的宏,允許使用 #define 進行定義。
定義的工作方式非常容易理解:編譯器將程式碼中出現的所有內容替換為定義的值。它使用以下語法 #define
有點像「Ctrl-f 並替換」。
我們可以使用定義來定義可以在程式碼中使用的函數。
我們可以有條件地聲明宏。
如果名稱已經定義,那麼我們執行以下程式碼。
在本例中,我使用了 #ifndef,但它也存在:
我們喜歡用大量評論來表示 #if 的結束。這是一個可以讓您更好地瀏覽程式碼的約定。
您可以在前面的範例中看到我使用了關鍵字 __FUNCTION__ 和 __LINE__。
正如您可以想像的,這些是編譯器將替換為正確值的巨集。
有一個常見預先定義巨集的清單。
請注意,有所謂的系統特定宏。
小型非詳盡清單:
在這裡,我們可以看到我們產生了可變參數宏,在建立日誌時特別有用。
(即使使用 printfs 產生日誌不是一個好主意。)
為此,我們必須建立一個外部文件,通常命名為 *.def,儘管沒有約定。
這種宏非常有用。我必須承認,它很少在原始程式碼中找到,但它允許您修改程式的操作,而無需修改原始程式碼。有趣的是,它經常用於創建核心。它允許您產生全域結構,例如 IDT 和 GDT。
注意:先快速澄清一下,宏是很棒的工具,但你必須小心。你絕對不應該使用這種類型的巨集:
舉例:MIN(2 5, fibo(25))
MIN(2 5, fibo(25)) => (2 5
這裡的問題是計算優先權。編譯器將首先執行比較,然後執行加法,因此為 2 (1)。我們透過使用巨集參數新增括號來修正此問題。
由於您永遠不知道使用者將傳遞什麼作為參數,因此請始終在參數上加上括號。
MIN(2 5, fibo(25)) => (2 5
我們注意到編譯器做了一個愚蠢而令人討厭的替換,這意味著我們將計算 fibo(25) 兩次。我讓你想像一下它是否是一個遞歸實現。
為了解決這個問題,我們在 if 之前宣告一個中間變數。
在這裡,這純粹是為了好玩而寫的多餘程式碼。我不一定建議您在程式碼中使用這些巨集。
我只是在享受樂趣(人生中的一件好事)。
我會讓你用一點 -fsanitize=address 來測試。真的很瘋狂。我們甚至可以看到 auto_free 函數的改進,它將結構名稱的字串作為參數來進行切換。
更多的 chill 函數,我們只計算函數的執行時間。對於基準測試非常有用。
小 X 宏,它將宏作為參數並對其進行擴展。
在這裡,我們實際上用巨集產生整個函數,因為 C 沒有限制。我也是?
現在是時候總結一下了。我們看到了很多很酷的東西。如果您有興趣,您可以自由地自己探索宏。還有很多東西值得一看。
所以,結論:RTFM。
PS: 至於標題,巨集不是遞歸的,它們僅以深度 1 擴展,在我們目前的情況下,GCC 將對 INC 進行隱式聲明並崩潰。
以上是#define INC(a) INC(a ?的詳細內容。更多資訊請關注PHP中文網其他相關文章!