C語言巨集定義 , 巧妙用法

2021-09-29 11:25:46 字數 4100 閱讀 1074

在我學習32的過程中發現了這樣一段**:

/*資訊輸出*/

#define eeprom_debug_on 1

#define eeprom_info(fmt,arg...) printf("<<-eeprom-info->> "fmt"\n",##arg)

#define eeprom_error(fmt,arg...) printf("<<-eeprom-error->> "fmt"\n",##arg)

#define eeprom_debug(fmt,arg...) dowhile(0)

這種**是參考linux核心寫出來,方便我們在32使用過程中檢測資訊,返回錯誤,以及程式除錯。我相信大部分人會發現,對於巨集的了解絕對沒有自己想像中的那麼多。

下面就來講講巨集:

c語言中的巨集定義

#define是預處理器處理的單元實體之一

#define定義的可以出現在程式的任意位置

#define定義之後的**都可以使用這個巨集

c語言中的巨集常量

#define定義的巨集常量可以直接使用

#define定義的巨集常量本質為字面量

1、巨集可以像大多數函式一樣被定義,我記得某公司出過這樣一道面試題,要求用巨集定義來比較大小,輸出最大值。

這道題可能看似很簡單我們只需要定義乙個巨集和兩個引數就可以了。

像這樣:#define max(x,y) x > y ? x : y

但要是只能寫出這樣的**只能說明我們有相應的基礎,但這還遠遠不夠。

要知道預處理器遇到巨集定義時原則上會全部展開,這樣執行上面的**似乎沒有什麼問題。

但當我們執行下例**時:printf("max=%d",max(1!=1,1!=2));我們會發現輸出的結果跟我們的預期是不一樣的,這是因為printf();函式展開後是這樣的:printf("max=%d",1!=1>1!=2?1!=1:1!=2);

我們可以將巨集改為#define max(x,y) (x) > (y) ? (x) : (y)到此這才算是乙個可以執行的巨集;

但此巨集還是有問題,本文主要講解「#」和「##」用法。

想要了解如何更好的寫出近乎完美的巨集定義可以參考:c語言僅憑自學能到什麼高度?.

2、當巨集自己呼叫自己時會發生什麼:

例:#define add(x) (add(x)+x)

如上巨集定義呼叫時會直接展開,我們可能會認為當前巨集會對自己無限遞迴。但c語言為了防止無限遞迴造成死迴圈。語法規定,當巨集在遇到自己時,就停止展開當前巨集,也就是說,add(1)的結果為:「add(1)+1」

3、在我使用中經常用到的巨集:條件編譯

先讓我們來看一段**:

#ifndef __i2c_ee_h

#define __i2c_ee_h

#include

"stm32f10x.h"

/**************************i2c引數定義,i2c1或i2c2********************************/

#define eeprom_i2cx i2c1

#define eeprom_i2c_apbxclock_fun rcc_apb1periphclockcmd

#define eeprom_i2c_clk rcc_apb1periph_i2c1

#define eeprom_i2c_gpio_apbxclock_fun rcc_apb2periphclockcmd

#define eeprom_i2c_gpio_clk rcc_apb2periph_gpiob

#define eeprom_i2c_scl_port gpiob

#define eeprom_i2c_scl_pin gpio_pin_6

#define eeprom_i2c_sda_port gpiob

#define eeprom_i2c_sda_pin gpio_pin_7

#endif

/* __i2c_ee_h */

這基本上是在我建立使用者函式時一定會用到它。因為,條件編譯可以解決標頭檔案重複編譯的錯誤。

#if... #else #endif則可以讓我們有選擇的去執行我們想要其執行的**段。

4、我們不曾知道的「#」,「##」。

「#」符號可以把乙個符號直接轉換為字串。

例:#define string(x) #x

char * str = string( string );

這段的意思就是str中的內容是字串string。

而「##」則會連線臨近的符號從而產生新的符號。

例:#define cons (x,y) (x**e**y)

printf("cons is %d\n",cons(2,3));

因為「##」會將相近的符號連線起來,所以這段**則會輸出 「cons is 2000」;

5、當巨集引數是另乙個巨集時會發生什麼?

巨集引數是另外乙個巨集時,會先展開作為引數的巨集,當展開的巨集引數被放進巨集體時,預處理器將會對新展開的巨集體進行展開。

例:#define a(x) x#define cons (x,y) (x**e**y)a(cons(2,3));2和3作為巨集cons的引數對cons進行展開為2000,再將展開的結果作為引數放入a(x)。

例外情況:當巨集主體對引數使用了「#」或「##」時,巨集將不會對引數進行展開。

例:#define tosrting(x) #x#define string(x) tosrting(x)

#define a(x) #x#define cons (x,y) (x**e**y)

char * str = string(a(cons(2,3)));可以試想一下str中儲存的字串為多少。

6、變參巨集,可以讓你定義類似的巨集:

#define eeprom_error(format, ... ) printf("<<-eeprom-info->> "format"\n",__va_args__)

eeprom_error("i2c 等待超時!errorcode = %d",errorcode);

__va_args__為系統預定義巨集,會被自動替換為引數列表。

/*資訊輸出*/

#define eeprom_debug_on 1

#define eeprom_info(fmt,arg...) printf("<<-eeprom-info->> "fmt"\n",##arg)

//eeprom_error("i2c 等待超時!errorcode = %d",errorcode);如何呼叫

#define eeprom_error(fmt,arg...) printf("<<-eeprom-error->> "fmt"\n",##arg)

#define eeprom_debug(fmt,arg...) dowhile(0)

相信大家可以很容易的知道,printf函式中各引數的意思了。

C語言 巨集定義的妙用

在 中為了提高可讀性,把一些數字進行巨集標識化,同時也方便後續修改 一處修改,到處生效 define year count sec 365 24 60 60 ul static u32 do and retry try cnt define do and retry func,target,try,...

do while 0 在巨集定義中的巧妙用法

大家都知道,do while condition 可以表示迴圈,但你有沒有遇到在一些巨集定義中可以不用迴圈的地方,也用到了 do while.比如 define delete pointer p do while 0 這時,do while 0 的功能就不僅僅是迴圈了,這是do.while 0 的一...

vim巧妙用法

1.塊複製 按ctrl v鍵,編輯框最下方將出現 可視 塊 字樣 使用方向鍵移動游標,選擇矩形區域內的文字 y 鍵複製文字 d 鍵剪下文字 p 鍵貼上文字 按shift v鍵,多行選擇 2.分屏 1 水平分屏 vim on file1 file2 o 水平 n 檔案數 切換 ctrl w w 水平分...