0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度解讀宏定義所有的知識(shí)點(diǎn)

Linux愛(ài)好者 ? 來(lái)源:IOT物聯(lián)網(wǎng)小鎮(zhèn) ? 作者:道哥 ? 2021-05-25 10:14 ? 次閱讀

一、前言

一直以來(lái),我都有這樣一種感覺(jué):當(dāng)我學(xué)習(xí)一個(gè)新領(lǐng)域的知識(shí)時(shí),如果其中的某個(gè)知識(shí)點(diǎn)在剛開(kāi)始接觸時(shí),我感覺(jué)比較難懂、不好理解,那么以后不論我花多長(zhǎng)時(shí)間去研究這個(gè)知識(shí)點(diǎn),心里會(huì)一直認(rèn)為該知識(shí)點(diǎn)比較難,也就是說(shuō)第一印象特別的重要。

就比如 C 語(yǔ)言中的宏定義,好像跟我犯沖一樣,我一直覺(jué)得宏定義是 C 語(yǔ)言中最難的部分,就好比有有些小伙伴一直覺(jué)得指針是 C 語(yǔ)言中最難的部分一樣。

宏的本質(zhì)就是代碼生成器,在預(yù)處理器的支持下實(shí)現(xiàn)代碼的動(dòng)態(tài)生成,具體的操作通過(guò)條件編譯和宏擴(kuò)展來(lái)實(shí)現(xiàn)。我們先在心中建立這么一個(gè)基本的概念,然后通過(guò)實(shí)際的描述和代碼來(lái)深入的體會(huì):如何駕馭宏定義。

所以,今天我們就來(lái)把宏定義所有的知識(shí)點(diǎn)進(jìn)行匯總、深挖,希望經(jīng)過(guò)這篇文章,我能夠擺脫心理的這個(gè)魔障??赐赀@篇總結(jié)文章后,我相信你也一定能夠?qū)甓x有一個(gè)總體、全局的把握。

二、預(yù)處理器的操作

1. 宏的生效環(huán)節(jié):預(yù)處理

一個(gè) C 程序在編譯的時(shí)候,從源文件開(kāi)始到最后生成二進(jìn)制可執(zhí)行文件,一共經(jīng)歷 4 個(gè)階段:

d582cb5c-bccb-11eb-9e57-12bb97331649.png

我們今天討論的內(nèi)容就是在第一個(gè)環(huán)節(jié):預(yù)處理,由預(yù)處理器來(lái)完成這個(gè)階段的工作,包括下面這 4 項(xiàng)工作:

文件引入(#include);

條件編譯(#if..#elif..#endif);

宏擴(kuò)展(macro expansions);

行控制(line control)。

2. 條件編譯

一般情況下,C 語(yǔ)言文件中的每一行代碼都是要被編譯的,但是有時(shí)候出于對(duì)程序代碼優(yōu)化的考慮,希望只對(duì)其中的一部分代碼進(jìn)行編譯,此時(shí)就需要在程序中加上條件,讓編譯器只對(duì)滿足條件的代碼進(jìn)行編譯,將不滿足條件的代碼舍棄,這就是條件編譯。

簡(jiǎn)單的說(shuō):就是預(yù)處理器根據(jù)我們?cè)O(shè)置的條件,對(duì)代碼進(jìn)行動(dòng)態(tài)的處理,把有效的代碼輸出到一個(gè)中間文件,然后送給編譯器進(jìn)行編譯。

條件編譯基本上在所有的項(xiàng)目代碼中都被使用到,例如:當(dāng)你需要考慮下面的幾種情況時(shí),就一定會(huì)使用條件編譯:

需要把程序編譯成不同平臺(tái)下的可執(zhí)行程序;

同一套代碼需要運(yùn)行在同一平臺(tái)上的不同功能產(chǎn)品上;

在程序中存在著一些測(cè)試目的的代碼,不想污染產(chǎn)品級(jí)的代碼,需要屏蔽掉。

這里舉 3 個(gè)例子,在代碼中經(jīng)??吹降年P(guān)于條件編譯:

示例1:用來(lái)區(qū)分 C 和 C++ 代碼

#ifdef __cplusplus extern “C” { #endif void hello(); #ifdef __cplusplus } #endif

這樣的代碼幾乎在每個(gè)開(kāi)源庫(kù)中都可能見(jiàn)到,主要的目的就是 C 和 C++ 混合編程,具體來(lái)說(shuō)就是:

如果使用 gcc 來(lái)編譯,那么宏 __cplusplus 將不存在,其中的 extern “C” 將會(huì)被忽略;

如果使用 g++ 來(lái)編譯,那么宏 __cplusplus 就存在,其中的 extern “C” 就發(fā)生作用,編譯出來(lái)的函數(shù)名 hello 就不會(huì)被 g++ 編譯器改寫(xiě),因此就可以被 C 代碼來(lái)調(diào)用;

示例2:用來(lái)區(qū)分不同的平臺(tái)

#if defined(linux) || defined(__linux) || defined(__linux__) sleep(1000 * 1000); // 調(diào)用 Linux 平臺(tái)下的庫(kù)函數(shù)#elif defined(WIN32) || defined(_WIN32) Sleep(1000 * 1000); // 調(diào)用 Windows 平臺(tái)下的庫(kù)函數(shù)(第一個(gè)字母是大寫(xiě))#endif

那么,這些 linux, __linux, __linux__, WIN32, _WIN32 是從哪里來(lái)的呢?我們可以認(rèn)為是編譯目標(biāo)平臺(tái)(操作系統(tǒng))為我們預(yù)先準(zhǔn)備好的。

示例3:在編寫(xiě) Windows 平臺(tái)下的動(dòng)態(tài)庫(kù)時(shí),聲明導(dǎo)出和導(dǎo)入函數(shù)

#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 // 函數(shù)聲明LIBA_API void hello();

這段代碼是直接從我之前在 B 站錄制的一個(gè)小視頻里的示例拿過(guò)來(lái)的,當(dāng)時(shí)主要是演示如何如何在 Linux 平臺(tái)下使用 make 和 cmake 構(gòu)建工具來(lái)編譯,后來(lái)又小伙伴讓我在 Windows 平臺(tái)下也用 make 和 cmake 來(lái)構(gòu)建,所以就寫(xiě)了上面這段宏定義。

在使用 MSVC 編譯動(dòng)態(tài)庫(kù)時(shí),需要在編譯選項(xiàng)(Makefle 或者 CMakeLists.txt)中定義宏 LIBA_API_EXPORTS,那么導(dǎo)出函數(shù) hello 的最前面的宏 LIBA_API 就會(huì)被替換成:__declspec(dllexport),表示導(dǎo)出操作;

在編譯應(yīng)用程序的時(shí)候,使用動(dòng)態(tài)庫(kù),需要 include 動(dòng)態(tài)庫(kù)的頭文件,此時(shí)在編譯選項(xiàng)中不需要定義宏 LIBA_API_EXPORTS,那么 hello 函數(shù)最前面的 LIBA_API 就會(huì)被替換成 __declspec(dllimport),表示導(dǎo)入操作;

補(bǔ)充一點(diǎn):如果使用靜態(tài)庫(kù),編譯選項(xiàng)中不需要任何宏定義,那么宏 LIBA_API 就為空。

3. 平臺(tái)預(yù)定義的宏

上面已經(jīng)看到了,目標(biāo)平臺(tái)會(huì)為我們預(yù)先定義好一些宏,方便我們?cè)诔绦蛑惺褂?。除了上面的操作系統(tǒng)相關(guān)宏,還有另一類宏定義,在日志系統(tǒng)中被廣泛的使用:

FILE:當(dāng)前源代碼文件名;

LINE:當(dāng)前源代碼的行號(hào);

FUNCTION:當(dāng)前執(zhí)行的函數(shù)名;

DATE:編譯日期;

TIME:編譯時(shí)間;

例如:

printf(“file name: %s, function name = %s, current line:%d ”, __FILE__, __FUNCTION__, __LINE__);

三、宏擴(kuò)展

所謂的宏擴(kuò)展就是代碼替換,這部分內(nèi)容也是我想表達(dá)的主要內(nèi)容。宏擴(kuò)展最大的好處有如下幾點(diǎn):

減少重復(fù)的代碼;

完成一些通過(guò) C 語(yǔ)法無(wú)法實(shí)現(xiàn)的功能(字符串拼接);

動(dòng)態(tài)定義數(shù)據(jù)類型,實(shí)現(xiàn)類似 C++ 中模板的功能;

程序更容易理解、修改(例如:數(shù)字、字符串常亮);

我們?cè)趯?xiě)代碼的時(shí)候,所有使用宏名稱的地方,都可以理解為一個(gè)占位符。在編譯程序的預(yù)處理環(huán)節(jié),這些宏名將會(huì)被替換成宏定義中的那些代碼段,注意:僅僅是單純的文本替換。

1. 最常見(jiàn)的宏

為了方便后面的描述,先來(lái)看幾個(gè)常見(jiàn)的宏定義:

(1) 數(shù)據(jù)類型的定義

#ifndef BOOL typedef char BOOL;#endif #ifndef TRUE #define TRUE#endif #ifndef FALSE #define FALSE#endif

在數(shù)據(jù)類型定義中,需要注意的一點(diǎn)是:如果你的程序需要用不同平臺(tái)下的編譯器來(lái)編譯,那么你要去查一下所使用的編譯器對(duì)這些宏定義控制的數(shù)據(jù)類型是否已經(jīng)定義了。例如:在 gcc 中沒(méi)有 BOOL 類型,但是在 MSVC 中,把 BOOL 類型定義為 int 型。

(2) 獲取最大、最小值

#define MAX(a, b) (((a) 》 (b)) ? (a) : (b))#define MIN(a, b) (((a) 《 (b)) ? (a) : (b))

(3) 計(jì)算數(shù)組中的元素個(gè)數(shù)

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

(4) 位操作

#define BIT_MASK(x) (1 《《 (x))#define BIT_GET(x, y) (((x) 》》 (y)) & 0x01u)#define BIT_SET(x, y) ((x) | (1 《《 (y)))#define BIT_CLR(x, y) ((x) & (~(1 《《 (y))))#define BIT_INVERT(x, y) ((x) ^ (1 《《 (y)))

2. 與函數(shù)的區(qū)別

從上面這幾個(gè)宏來(lái)看,所有的這些操作都可以通過(guò)函數(shù)來(lái)實(shí)現(xiàn),那么他們各有什么優(yōu)缺點(diǎn)呢?

通過(guò)函數(shù)來(lái)實(shí)現(xiàn):

形參的類型需要確定,調(diào)用時(shí)對(duì)參數(shù)進(jìn)行檢查;

調(diào)用函數(shù)時(shí)需要額外的開(kāi)銷(xiāo):操作函數(shù)棧中的形參、返回值等;

通過(guò)宏來(lái)實(shí)現(xiàn):

不需要檢查參數(shù),更靈活的傳參;

直接對(duì)宏進(jìn)行代碼擴(kuò)展,執(zhí)行時(shí)不需要函數(shù)調(diào)用;

如果同一個(gè)宏在多處調(diào)用,會(huì)增加代碼體積;

還是舉一個(gè)例子來(lái)說(shuō)明比較好,就拿上面的比較大小來(lái)說(shuō)吧:

(1) 使用宏來(lái)實(shí)現(xiàn)

#define MAX(a, b) (((a) 》 (b)) ? (a) : (b)) int main(){ printf(“max: %d ”, MAX(1, 2));}

(2) 使用函數(shù)來(lái)實(shí)現(xiàn)

int max(int a, int b){ if (a 》 b) return a; return b;} int main(){ printf(“max: %d ”, max(1, 2));}

除了函數(shù)調(diào)用的開(kāi)銷(xiāo),其它看起來(lái)沒(méi)有差別。這里比較的是 2 個(gè)整型數(shù)據(jù),那么如果還需要比較 2 個(gè)浮點(diǎn)型數(shù)據(jù)呢?

使用宏來(lái)調(diào)用:MAX(1.1, 2.2);一切 OK;

使用函數(shù)調(diào)用:max(1.1, 2.2); 編譯報(bào)錯(cuò):類型不匹配。

此時(shí),使用宏來(lái)實(shí)現(xiàn)的優(yōu)勢(shì)就體現(xiàn)出來(lái)了:因?yàn)楹曛袥](méi)有類型的概念,調(diào)用者傳入任何數(shù)據(jù)類型都可以,然后在后面的比較操作中,大于或小于操作都是利用了 C 語(yǔ)言本身的語(yǔ)法來(lái)執(zhí)行。

如果使用函數(shù)來(lái)實(shí)現(xiàn),那么就必須再定義一個(gè)用來(lái)操作浮點(diǎn)型的函數(shù),以后還有可能比較:char 型、long 型數(shù)據(jù)等等。

在 C++ 中,這樣的操作可以通過(guò)參數(shù)模板來(lái)實(shí)現(xiàn),所謂的模板也是一種代碼動(dòng)態(tài)生成機(jī)制。當(dāng)定義了一個(gè)函數(shù)模板后,根據(jù)調(diào)用者的實(shí)參,來(lái)動(dòng)態(tài)產(chǎn)生多個(gè)函數(shù)。例如定義下面這個(gè)函數(shù)模板:

template《typename T》 T max(T a, T b){ if (a 》 b) return a; return b;} max(1, 2); // 實(shí)參是整型max(1.1, 2,2); // 實(shí)參是浮點(diǎn)型

當(dāng)編譯器看到 max(1, 2) 時(shí),就會(huì)動(dòng)態(tài)生成一個(gè)函數(shù) int max(int a, int b) { 。.. };

當(dāng)編譯器看到 max(1.1, 2.2) 時(shí),又會(huì)動(dòng)態(tài)生成另一個(gè)函數(shù) float max(float a, float b) { 。.. }。

所以,從代碼的動(dòng)態(tài)生成角度看,宏定義和 C++ 中的模板參數(shù)有點(diǎn)神似,只不過(guò)宏定義僅僅是代碼擴(kuò)展而已。

下面這個(gè)例子也比較不錯(cuò),利用宏的類型無(wú)關(guān),來(lái)動(dòng)態(tài)生成結(jié)構(gòu)體:

#define VEC(T) struct vector_##T { T *data; size_t size; }; int main(){ VEC(int) vec_1 = { .data = NULL, .size = 0 }; VEC(float) vec_2 = { .data = NULL, .size = 0 };}

這個(gè)例子中用到了 ##,下面會(huì)解釋這個(gè)知識(shí)點(diǎn)。在前面的例子中,宏的參數(shù)傳遞的都是一些變量,而這里傳遞的宏參數(shù)是數(shù)據(jù)類型,通過(guò)宏的類型無(wú)關(guān)性,達(dá)到了“動(dòng)態(tài)”創(chuàng)建結(jié)構(gòu)體的目的:

struct vector_int { int *data; size_t size;} struct vector_float { float *data; size_t size;}

這里有一個(gè)陷阱需要注意:傳遞的數(shù)據(jù)類型中不能有空格,如果這樣使用:VEC(long long),那替換之后得到:

struct vector_long long { // 語(yǔ)法錯(cuò)誤 long long *data; size_t size;}

四、符號(hào):# 與 ##

這兩個(gè)符號(hào)在編程中的作用也是非常巧妙,夸張的說(shuō)一句:在任何框架性代碼中,都能見(jiàn)到它們的身影!作用如下:

#:把參數(shù)轉(zhuǎn)換成字符串;

##:連接參數(shù)。

1. #: 字符串化

直接看最簡(jiǎn)單的例子:

#define STR(x) #xprintf(“string of 123: %s ”, STR(123));

傳入的是一個(gè)數(shù)字 123,輸出的結(jié)果是字符串 “123”,這就是字符串化。

2. ##:參數(shù)連接

把宏中的參數(shù)按照字符進(jìn)行拼接,從而得到一個(gè)新的標(biāo)識(shí)符,例如:

#define MAKE_VAR(name, no) name##no int main(void){ int MAKE_VAR(a, 1) = 1; int MAKE_VAR(b, 2) = 2; printf(“a1 = %d ”, a1); printf(“b2 = %d ”, b2); return 0;}

當(dāng)調(diào)用宏 MAKE_VAR(a, 1) 后,符號(hào) ## 把兩側(cè)的 name 和 no 首先替換為 a 和 1,然后連接得到 a1。然后在調(diào)用語(yǔ)句中前面的 int 數(shù)據(jù)類型就說(shuō)明了 a1 是一個(gè)整型數(shù)據(jù),最后初始化為 1。

五、可變參數(shù)的處理

1. 參數(shù)名的定義和使用

宏定義的參數(shù)個(gè)數(shù)可以是不確定的,就像調(diào)用 printf 打印函數(shù)一樣,在定義的時(shí)候,可以使用三個(gè)點(diǎn)(。..)來(lái)表示可變參數(shù),也可以在三個(gè)點(diǎn)的前面加上可變參數(shù)的名稱。

如果使用三個(gè)點(diǎn)(。..)來(lái)接收可變參數(shù),那么在使用的時(shí)候就需要使用__VA_ARGS__來(lái)表示可變參數(shù),如下:

#define debug1(。..) printf(__VA_ARGS__) debug1(“this is debug1: %d ”, 1);

如果在三個(gè)點(diǎn)(。..)的前面加上了一個(gè)參數(shù)名,那么在使用時(shí)就一定要使用這個(gè)參數(shù)名,而不能使用__VA_ARGS__來(lái)表示可變參數(shù),如下:

#define debug2(args.。.) printf(args) debug1(“this is debug2: %d ”, 2);

2. 可變參數(shù)個(gè)數(shù)為零的處理

看一下這個(gè)宏:

#define debug3(format, 。..) printf(format, __VA_ARGS__) debug3(“this is debug4: %d ”, 4);

編譯、執(zhí)行都沒(méi)有問(wèn)題。但是如果這樣來(lái)使用宏:

debug3(“hello ”);

編譯的時(shí)候,會(huì)出現(xiàn)錯(cuò)誤: error: expected expression before ‘)’ token。為什么呢?

看一下宏擴(kuò)展之后的代碼(__VA_ARGS__為空):

printf(“hello ”,);

看出問(wèn)題了吧?在格式化字符串的后面多了一個(gè)逗號(hào)!為了解決問(wèn)題,預(yù)處理器給我們提供了一個(gè)方法:通過(guò) ## 符號(hào)把這個(gè)多余的逗號(hào)給自動(dòng)刪掉。于是宏定義改成下面這樣就沒(méi)有問(wèn)題了。

#define debug3(format, 。..) printf(format, ##__VA_ARGS__)

類似的,如果自己定義了可變參數(shù)的名字,也在前面加上 ##,如下:

#define debug4(format, args.。.) printf(format, ##args)

六、奇思妙想的宏

宏擴(kuò)展的本質(zhì)就是文本替換,但是一旦加上可變參數(shù)(__VA_ARGS__)和 ## 的連接功能,就能夠變化出無(wú)窮的想象力。

我一直堅(jiān)信,模仿是成為高手的第一步,只有見(jiàn)多識(shí)廣、多看、多學(xué)習(xí)別人是怎么來(lái)使用宏的,然后拿來(lái)為己所用,按照“先僵化-再優(yōu)化-最后固化”這個(gè)步驟來(lái)訓(xùn)練,總有一天你也能成為高手。

這里我們就來(lái)看幾個(gè)利用宏定義的巧妙實(shí)現(xiàn)。

1. 日志功能

在代碼中添加日志功能,幾乎是每個(gè)產(chǎn)品的標(biāo)配了,一般見(jiàn)到最普遍的是下面這樣的用法:

#ifdef DEBUG #define LOG(。..) printf(__VA_ARGS__)#else #define LOG(。..) #endif int main(){ LOG(“name = %s, age = %d ”, “zhangsan”, 20); return 0;}

在編譯的時(shí)候,如果需要輸出日志功能就傳入宏定義 DEBUG,這樣就能打印輸出調(diào)試信息,當(dāng)然實(shí)際的產(chǎn)品中需要寫(xiě)入到文件中。如果不需要打印語(yǔ)句,通過(guò)把打印日志信息那條語(yǔ)句定義為空語(yǔ)句來(lái)達(dá)到目的。

換個(gè)思路,我們還可以通過(guò)條件判斷語(yǔ)句來(lái)控制打印信息,如下:

#ifdef DEBUG #define debug if(1)#else #define debug if(0)#endif int main(){ debug { printf(“name = %s, age = %d ”, “zhangsan”, 20); } return 0;}

這樣控制日志信息的看到的不多,但是也能達(dá)到目的,放在這里只是給大家開(kāi)闊一下思路。

2. 利用宏來(lái)迭代每個(gè)參數(shù)

#define first(x, 。..) #x#define rest(x, 。..) #__VA_ARGS__ #define destructive(。..) do { printf(“first is: %s ”, first(__VA_ARGS__)); printf(“rest are: %s ”, rest(__VA_ARGS__)); } while (0) int main(void){ destructive(1, 2, 3); return 0;}

主要的思想就是:每次把可變參數(shù)VA_ARGS中的第一個(gè)參數(shù)給分離出來(lái),然后把后面的參數(shù)再遞歸處理,這樣就可以分離出每一個(gè)參數(shù)了。我記得侯杰老師在 C++ 的視屏中,利用可變參數(shù)模板這個(gè)語(yǔ)法,也實(shí)現(xiàn)了類似的功能。

剛才在有道筆記中居然找到了侯杰老師演示的代碼,熟悉 C++ 的小伙伴可以研究下下面這段代碼:

// 遞歸的最后一次調(diào)用void myprint(){} template 《typename T, typename.。. Types》void myprint(const T &first, const Types&.。. args){ std::cout 《《 first 《《 std::endl; std::cout 《《 “remain args size = ” 《《 sizeof.。.(args) 《《 std::endl; // 把其他參數(shù)遞歸調(diào)用 myprint(args.。.);} int main(){ myprint(“aaa”, 7.5, 100); return 0;}

3. 動(dòng)態(tài)的調(diào)用不同的函數(shù)

// 普通的枚舉類型enum { ERR_One, ERR_Two, ERR_Three}; // 利用 ## 的拼接功能,動(dòng)態(tài)產(chǎn)生 case 中的比較值,以及函數(shù)名。#define TEST(no) case ERR_##no: Func_##no(); break; void Func_One(){ printf(“this is Func_One ”);} void Func_Two(){ printf(“this is Func_Two ”);} void Func_Three(){ printf(“this is Func_Three ”);} int main(){ int c = ERR_Two; switch (c) { TEST(One); TEST(Two); TEST(Three); }; return 0;}

在這個(gè)例子中,核心在于 TEST 宏定義,通過(guò) ## 拼接功能,構(gòu)造出 case 分支的比較目標(biāo),然后動(dòng)態(tài)拼接得到對(duì)應(yīng)的函數(shù),最后調(diào)用這個(gè)函數(shù)。

4. 動(dòng)態(tài)創(chuàng)建錯(cuò)誤編碼與對(duì)應(yīng)的錯(cuò)誤字符串

這也是一個(gè)非常巧妙的例子,利用了 #(字符串化) 和 ##(拼接) 這 2 個(gè)功能來(lái)動(dòng)態(tài)生成錯(cuò)誤編碼碼和相應(yīng)的錯(cuò)誤字符串:

#define MY_ERRORS E(TOO_SMALL) E(TOO_BIG) E(INVALID_VARS) #define E(e) Error_## e,typedef enum { MY_ERRORS} MyEnums;#undef E #define E(e) #e,const char *ErrorStrings[] = { MY_ERRORS};#undef E int main(){ printf(“%d - %s ”, Error_TOO_SMALL, ErrorStrings[0]); printf(“%d - %s ”, Error_TOO_BIG, ErrorStrings[1]); printf(“%d - %s ”, Error_INVALID_VARS, ErrorStrings[2]); return 0;}

我們把宏展開(kāi)之后,得到一個(gè)枚舉類型和一個(gè)字符串常量數(shù)組:

typedef enum { Error_TOO_SMALL, Error_TOO_BIG, Error_INVALID_VARS,} MyEnums; const char *ErrorStrings[] = { “TOO_SMALL”, “TOO_BIG”, “INVALID_VARS”,};

宏擴(kuò)展之后的代碼是不是很簡(jiǎn)單啊。編譯、執(zhí)行結(jié)果如下:

0 - TOO_SMALL 1 - TOO_BIG 2 - INVALID_VARS

七、總結(jié)

有些人對(duì)宏愛(ài)之要死,多到濫用的程度;而有些人對(duì)宏恨之入骨,甚至用上了邪惡(evil)這個(gè)詞!其實(shí)宏對(duì)于 C 來(lái)說(shuō),就像菜刀對(duì)于廚師和歹徒一樣:用的好,可以讓代碼結(jié)構(gòu)簡(jiǎn)潔、后期維護(hù)特別方便;用的不好,就會(huì)引入晦澀的語(yǔ)法、難以調(diào)試的 Bug。

對(duì)于我們開(kāi)發(fā)人員來(lái)說(shuō),只要在程序的執(zhí)行效率、代碼的可維護(hù)性上做好平衡就可以了。

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4723

    瀏覽量

    68237
  • C 語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    14220

原文標(biāo)題:提高代碼逼格的利器:宏定義-從入門(mén)到放棄

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    C語(yǔ)言中最常見(jiàn)的定義寫(xiě)法

    如果讓你用C語(yǔ)言寫(xiě)個(gè)定義,我相信大部分同學(xué)順手就能寫(xiě)出define。
    的頭像 發(fā)表于 10-28 11:12 ?166次閱讀

    SV中define定義的用法

    SV中使用預(yù)處理指令`define來(lái)定義,可以用來(lái)創(chuàng)建文本替換。根據(jù)場(chǎng)景不同,`define主要用來(lái)定義常量、簡(jiǎn)化復(fù)雜的表達(dá)式或代碼段以及提高代碼的可移植性。其基本語(yǔ)法為:
    的頭像 發(fā)表于 10-21 14:22 ?196次閱讀

    模擬電子技術(shù)知識(shí)點(diǎn)問(wèn)題總結(jié)概覽

    給大家分享模擬電子技術(shù)知識(shí)點(diǎn)問(wèn)題總結(jié)。
    的頭像 發(fā)表于 05-08 15:16 ?1080次閱讀
    模擬電子技術(shù)<b class='flag-5'>知識(shí)點(diǎn)</b>問(wèn)題總結(jié)概覽

    一篇搞定DCS系統(tǒng)相關(guān)知識(shí)點(diǎn)

    目標(biāo)。DCS系統(tǒng)廣泛應(yīng)用于各個(gè)行業(yè),如化工、電力、制藥等。在這些行業(yè)中,DCS系統(tǒng)可以實(shí)現(xiàn)對(duì)生產(chǎn)過(guò)程的集中監(jiān)控和分散控制,提高生產(chǎn)效率和產(chǎn)品質(zhì)量,降低能耗和減少環(huán)境污染,從而保證產(chǎn)品質(zhì)量,并確保生產(chǎn)過(guò)程的安全可靠。 二.DCS系統(tǒng)知識(shí)點(diǎn)
    的頭像 發(fā)表于 03-26 18:40 ?781次閱讀
    一篇搞定DCS系統(tǒng)相關(guān)<b class='flag-5'>知識(shí)點(diǎn)</b>

    【量子計(jì)算機(jī)重構(gòu)未來(lái) | 閱讀體驗(yàn)】第二章關(guān)鍵知識(shí)點(diǎn)

    本帖最后由 oxlm_1 于 2024-3-6 23:20 編輯 之所以將第二章單獨(dú)拿出來(lái),是因?yàn)樵陂喿x過(guò)程中,發(fā)現(xiàn)第二章知識(shí)點(diǎn)較多,理解起來(lái)比較耗時(shí)間。 第二章的主要知識(shí)點(diǎn): 量子
    發(fā)表于 03-06 23:17

    淺談初級(jí)電工必備知識(shí)點(diǎn)

    對(duì)于初學(xué)電工的朋友來(lái)說(shuō),掌握一些基礎(chǔ)且實(shí)用的知識(shí)點(diǎn)是非常重要的。本文旨在分享初級(jí)電工應(yīng)該掌握的核心知識(shí),幫助新手電工更好地入門(mén)和提升技能。
    的頭像 發(fā)表于 12-26 10:44 ?1006次閱讀

    TCP協(xié)議面試常問(wèn)知識(shí)點(diǎn)總結(jié)

    TCP 作為傳輸層的協(xié)議,是一個(gè)IT工程師素養(yǎng)的體現(xiàn),也是面試中經(jīng)常被問(wèn)到的知識(shí)點(diǎn)。在此,我將 TCP 核心的一些問(wèn)題梳理了一下,希望能幫到各位。
    的頭像 發(fā)表于 12-15 10:38 ?751次閱讀
    TCP協(xié)議面試常問(wèn)<b class='flag-5'>知識(shí)點(diǎn)</b>總結(jié)

    開(kāi)關(guān)模式下的電源電流如何檢測(cè)?這12個(gè)電路&amp;10個(gè)知識(shí)點(diǎn)講明白了

    開(kāi)關(guān)模式下的電源電流如何檢測(cè)?這12個(gè)電路&10個(gè)知識(shí)點(diǎn)講明白了
    的頭像 發(fā)表于 12-06 16:04 ?742次閱讀
    開(kāi)關(guān)模式下的電源電流如何檢測(cè)?這12個(gè)電路&amp;10個(gè)<b class='flag-5'>知識(shí)點(diǎn)</b>講明白了

    干貨 | 搞硬件設(shè)計(jì)不能忘記的11個(gè)知識(shí)點(diǎn)

    干貨 | 搞硬件設(shè)計(jì)不能忘記的11個(gè)知識(shí)點(diǎn) 1、不要忘記在 電源 輸入和輸出端加電容濾波通常情況,電源的輸入和輸出端的電信號(hào)是不穩(wěn)定的,直接給負(fù)載供電,長(zhǎng)期會(huì)給負(fù)載造成損傷,也會(huì)其使工作不穩(wěn)定。而我
    發(fā)表于 11-30 15:01

    c語(yǔ)言程序設(shè)計(jì)基礎(chǔ)知識(shí)點(diǎn)

    程序設(shè)計(jì)的基礎(chǔ)知識(shí)點(diǎn)。 首先,我們將從C語(yǔ)言的數(shù)據(jù)類型和變量開(kāi)始。C語(yǔ)言提供了多種數(shù)據(jù)類型,包括整數(shù)、浮點(diǎn)數(shù)、字符和指針等。整數(shù)類型包括int、long和short等,浮點(diǎn)數(shù)類型包括float和double等,字符類型用于存儲(chǔ)ASCII字符,指針類型用
    的頭像 發(fā)表于 11-27 15:25 ?1572次閱讀

    數(shù)字電位計(jì)知識(shí)點(diǎn)

    電子發(fā)燒友網(wǎng)站提供《數(shù)字電位計(jì)知識(shí)點(diǎn).pdf》資料免費(fèi)下載
    發(fā)表于 11-24 16:08 ?7次下載
    數(shù)字電位計(jì)<b class='flag-5'>知識(shí)點(diǎn)</b>

    define定義

    define定義 以#號(hào)開(kāi)頭的都是編譯預(yù)處理指令,它們不是C語(yǔ)言的成分,但是C程序離不開(kāi)它們,#define用來(lái)定義一個(gè),程序在預(yù)處理階段將用define
    的頭像 發(fā)表于 11-24 15:35 ?765次閱讀

    三菱和西門(mén)子PLC輸入接線知識(shí)點(diǎn)

    三菱和西門(mén)子PLC輸入接線知識(shí)點(diǎn)
    的頭像 發(fā)表于 11-21 10:01 ?696次閱讀
    三菱和西門(mén)子PLC輸入接線<b class='flag-5'>知識(shí)點(diǎn)</b>

    OFDM技術(shù)知識(shí)點(diǎn)

    電子發(fā)燒友網(wǎng)站提供《OFDM技術(shù)知識(shí)點(diǎn).rar》資料免費(fèi)下載
    發(fā)表于 11-18 14:25 ?1次下載
    OFDM技術(shù)<b class='flag-5'>知識(shí)點(diǎn)</b>

    詳細(xì)聊聊container_of這個(gè)定義

    大家周末好,我是bug菌~ 今天主要是跟大家詳細(xì)聊聊container_of這個(gè)定義,非常經(jīng)典的,只是一直沒(méi)有抽時(shí)間細(xì)細(xì)品味,今天就跟大家一起來(lái)看看有何神奇之處。
    的頭像 發(fā)表于 11-13 09:45 ?645次閱讀