如何提高代碼逼格?宏定義-從入門到放棄
一、前言
二、預處理器的操作
三、宏擴展
四、符號:# 與 ##
五、可變參數的處理
六、奇思妙想的宏
七、總結
一、前言
一直以來,我都有這樣一種感覺:當我學習一個新領域的知識時,如果其中的某個知識點在剛開始接觸時,我感覺比較難懂、不好理解,那么以后不論我花多長時間去研究這個知識點,心里會一直認為該知識點比較難,也就是說第一印象特別的重要。
就比如 C 語言中的宏定義,好像跟我犯沖一樣,我一直覺得宏定義是 C 語言中最難的部分,就好比有有些小伙伴一直覺得指針是 C 語言中最難的部分一樣。
宏的本質就是代碼生成器,在預處理器的支持下實現代碼的動態生成,具體的操作通過條件編譯和宏擴展來實現。我們先在心中建立這么一個基本的概念,然后通過實際的描述和代碼來深入的體會:如何駕馭宏定義。
所以,今天我們就來把宏定義所有的知識點進行匯總、深挖,希望經過這篇文章,我能夠擺脫心理的這個魔障。看完這篇總結文章后,我相信你也一定能夠對宏定義有一個總體、全局的把握。
二、預處理器的操作
1. 宏的生效環節:預處理
一個 C 程序在編譯的時候,從源文件開始到最后生成二進制可執行文件,一共經歷 4 個階段:

我們今天討論的內容就是在第一個環節:預處理,由預處理器來完成這個階段的工作,包括下面這 4 項工作:
文件引入(#include);條件編譯(#if..#elif..#endif);宏擴展(macro expansions);行控制(line control)。2. 條件編譯
一般情況下,C 語言文件中的每一行代碼都是要被編譯的,但是有時候出于對程序代碼優化的考慮,希望只對其中的一部分代碼進行編譯,此時就需要在程序中加上條件,讓編譯器只對滿足條件的代碼進行編譯,將不滿足條件的代碼舍棄,這就是條件編譯。
簡單的說:就是預處理器根據我們設置的條件,對代碼進行動態的處理,把有效的代碼輸出到一個中間文件,然后送給編譯器進行編譯。
條件編譯基本上在所有的項目代碼中都被使用到,例如:當你需要考慮下面的幾種情況時,就一定會使用條件編譯:
需要把程序編譯成不同平臺下的可執行程序;同一套代碼需要運行在同一平臺上的不同功能產品上;在程序中存在著一些測試目的的代碼,不想污染產品級的代碼,需要屏蔽掉。
這里舉 3 個例子,在代碼中經常看到的關于條件編譯:
示例1:用來區分 C 和 C++ 代碼
#ifdef __cplusplus extern "C" { #endif void hello(); #ifdef __cplusplus } #endif
這樣的代碼幾乎在每個開源庫中都可能見到,主要的目的就是 C 和 C++ 混合編程,具體來說就是:
如果使用 gcc 來編譯,那么宏 __cplusplus 將不存在,其中的 extern "C" 將會被忽略;如果使用 g++ 來編譯,那么宏 __cplusplus 就存在,其中的 extern "C" 就發生作用,編譯出來的函數名 hello 就不會被 g++ 編譯器改寫,因此就可以被 C 代碼來調用;
示例2:用來區分不同的平臺
#if defined(linux) || defined(__linux) || defined(__linux__) sleep(1000 * 1000); // 調用 Linux 平臺下的庫函數#elif defined(WIN32) || defined(_WIN32) Sleep(1000 * 1000); // 調用 Windows 平臺下的庫函數(第一個字母是大寫)#endif
那么,這些 linux, __linux, __linux__, WIN32, _WIN32 是從哪里來的呢?我們可以認為是編譯目標平臺(操作系統)為我們預先準備好的。
示例3:在編寫 Windows 平臺下的動態庫時,聲明導出和導入函數
#if defined(linux) || defined(__linux) || defined(__linux__) #define LIBA_API #else #ifdef LIBA_STATIC #define LIBA_API #else #ifdef LIBA_API_EXPORTS #define LIBA_API __declspec(dllexport) #else #define LIBA_API __declspec(dllimport) #endif #endif#endif
// 函數聲明LIBA_API void hello();
這段代碼是直接從我之前在 B 站錄制的一個小視頻里的示例拿過來的,當時主要是演示如何如何在 Linux 平臺下使用 make 和 cmake 構建工具來編譯,后來又小伙伴讓我在 Windows 平臺下也用 make 和 cmake 來構建,所以就寫了上面這段宏定義。
在使用 MSVC 編譯動態庫時,需要在編譯選項(Makefle 或者 CMakeLists.txt)中定義宏 LIBA_API_EXPORTS,那么導出函數 hello 的最前面的宏 LIBA_API 就會被替換成:__declspec(dllexport),表示導出操作;在編譯應用程序的時候,使用動態庫,需要 include 動態庫的頭文件,此時在編譯選項中不需要定義宏 LIBA_API_EXPORTS,那么 hello 函數最前面的 LIBA_API 就會被替換成 __declspec(dllimport),表示導入操作;補充一點:如果使用靜態庫,編譯選項中不需要任何宏定義,那么宏 LIBA_API 就為空。3. 平臺預定義的宏
上面已經看到了,目標平臺會為我們預先定義好一些宏,方便我們在程序中使用。除了上面的操作系統相關宏,還有另一類宏定義,在日志系統中被廣泛的使用:
FILE:當前源代碼文件名;
LINE:當前源代碼的行號;
FUNCTION:當前執行的函數名;
DATE:編譯日期;
TIME:編譯時間;
例如:
printf("file name: %s, function name = %s, current line:%d ", __FILE__, __FUNCTION__, __LINE__);
請輸入評論內容...
請輸入評論/評論長度6~500個字
最新活動更多
- 1 AI狂歡遇上油價破百,全球股市還能漲多久? | 產聯看全球
- 2 OpenAI深夜王炸!ChatGPT Images 2.0實測:中文穩、細節炸,設計師慌了
- 3 6000億美元估值錨定:字節跳動的“去單一化”突圍與估值重構
- 4 Tesla AI5芯片最新進展總結
- 5 連夜測了一波DeepSeek-V4,我發現它可能只剩“審美”這個短板了
- 6 熱點丨AI“瑜亮之爭”:既生OpenClaw,何生Hermes?
- 7 AI界的殺豬盤:9秒刪庫跑路,全員被封號,還繼續扣錢!
- 8 2026,人形機器人只贏了面子
- 9 DeepSeek降價90%:價格屠夫不是身份,是戰略
- 10 AI Infra產業鏈卡在哪里了?


分享













