define巨集定義中的 , , 及 符號

2021-07-30 09:59:41 字數 4176 閱讀 1445

1、# (

stringizing

字串化操作符。其作用是:將巨集定義中的傳入引數名轉換成用一對雙引號括起來引數名字串。其只能用於有傳入引數的巨集定義中,且必須置於巨集定義體中的引數名前。 如:

#define example(instr) printf("the input string is:/t%s/n",#instr)

#define example1(instr) #instr

當使用該巨集定義時:

example(abc); 在編譯時將會展開成:printf("the input string is:/t%s/n","abc");

string str=example1(abc); 將會展成:string str="abc";

注意:

對空格的處理

a。忽略傳入引數名前面和後面的空格。

如:str=example1(   abc ); 將會被擴充套件成 str="abc";

b.當傳入引數名間存在空格時,編譯器將會自動連線各個子字串,用每個子字串中只以乙個空格連線,忽略其中多餘乙個的空格。

如:str=exapme( abc    def); 將會被擴充套件成 str="abc def";

2、## (

token-pasting

符號連線操作符

巨集定義中:引數名,即為形參,如#define sum(a,b) (a+b);中a和b均為某一引數的代表符號,即形式引數。

而##的作用則是將巨集定義的多個形參成乙個實際引數名。如:

#define examplenum(n) num##n

int num9=9;

使用:int num=examplenum(9); 將會擴充套件成 int num=num9;

注意:1.當用##連線形參時,##前後的空格可有可無。

如:#define examplenum(n) num ## n 相當於 #define examplenum(n) num##n

2.連線後的實際引數名,必須為實際存在的引數名或是編譯器已知的巨集定義

// preprocessor_token_pasting.cpp

#include 

#define paster( n ) printf_s( "token" #n " = %d", token##n )

int token9 = 9;

int main()

執行結果:

token9 = 9

另外,如果##後的引數本身也是乙個巨集的話,##會阻止這個巨集的展開。

#define strcpy(a, b)    strcpy(a ## _p, #b)

int main()

3、@# (

charizing

字元化操作符

只能用於有傳入引數的巨集定義中

,且必須置於巨集定義體中的引數名前。作用

是將傳的單字元引數名轉換成字元

,以一對單引用括起來。

#define makechar(x)  #@x

a = makechar(b);

展開後變成了:

a= 'b';

4、\

行繼續操作符

當定義的巨集不能用一行表達完整時,可以用"\"表示下一行繼續此巨集的定義。

另:關於其他網友對##和#的補充

1. 簡單的說,「##」是一種分隔連線方式,它的作用是先分隔,然後進行強制連線。

其中,分隔的作用類似於空格。我們知道在普通的巨集定義中,預處理器一般把空格

解釋成分段標誌,對於每一段和前面比較,相同的就被替換。但是這樣做的結果是,

被替換段之間存在一些空格。如果我們不希望出現這些空格,就可以通過新增一些

##來替代空格。

另外一些分隔標誌是,包括操作符,比如 +, -, *, /, [,], ...,所以儘管下面的

巨集定義沒有空格,但是依然表達有意義的定義: define add(a, b)  a+b

而其強制連線的作用是,去掉和前面的字串之間的空格,而把兩者連線起來。

2. 舉列 -- 試比較下述幾個巨集定義的區別

#define a1(name, type)  type name_##type##_type 或

#define a2(name, type)  type name##_##type##_type

a1(a1, int);  /* 等價於: int name_int_type; */

a2(a1, int);  /* 等價於: int a1_int_type;   */

(這段話對##的解釋更加的清楚直白)

解釋:1) 在第乙個巨集定義中,"name"和第乙個"_"之間,以及第2個"_"和第二個

"type"之間沒有被分隔,所以預處理器會把name_##type##_type解釋成3段:

「name_」、「type」、以及「_type」,這中間只有「type」是在巨集前面出現過

的,所以它可以被巨集替換。

2) 而在第二個巨集定義中,「name」和第乙個「_」之間也被分隔了,所以

預處理器會把name##_##type##_type解釋成4段:「name」、「_」、「type」

以及「_type」,這其間,就有兩個可以被巨集替換了。

3) a1和a2的定義也可以如下:

#define a1(name, type)  type name_  ##type ##_type  

<##前面隨意加上一些空格》

#define a2(name, type)  type name ##_ ##type ##_type

結果是## 會把前面的空格去掉完成強連線,得到和上面結果相同的巨集定義

3. 其他相關 -- 單獨的乙個 #

至於單獨乙個#,則表示 對這個變數替換後,再加雙引號引起來。比如

#define  __stringify_1(x)   #x

那麼__stringify_1(

linux

)   <==>  "linux"

所以,對於module_device_table

1) #define module_device_table(type,name)                        

module_generic_table(type##_device,name)

2) #define module_generic_table(gtype,name)                      

extern const struct gtype##_id __mod_##gtype##_table     

__attribute__ ((unused, alias(__stringify(name))))

得到  

module_device_table(usb, products)  

/*notes: struct usb_device_id products; */

<==> module_generic_table(usb_device,products)

<==> extern const struct usb_device_id __mod_usb_device_table     

__attribute__ ((unused, alias("products")))   

注意到alias attribute需要乙個雙引號,所以在這裡使用了__stringify(name)來

給name加上雙引號。另外,還注意到乙個外部變數"__mod_usb_device_table"被alias

到了本驅動專用的由使用者自定義的變數products

是如何使用的,更多的資訊請參看《probe()過程分析》。

4. 分析方法和驗證方式 -- 編寫乙個簡單的c程式

用巨集定義乙個變數,同時用直接方式定義乙個相同的變數,編譯報告重複定義;

用巨集定義乙個變數,直接使用該巨集定義的變數名稱,編譯通過且執行結果正確;

使用printf列印字串資料。printf("token macro is %s", __stringify_1(a1));

define巨集定義中的 , , 及 符號

1 stringizing 字串化操作符。其作用是 將巨集定義中的傳入引數名轉換成用一對雙引號括起來引數名字串。其只能用於有傳入引數的巨集定義中,且必須置於巨集定義體中的引數名前。如 define example instr printf the input string is t s n inst...

define巨集定義中的 , , 及 符號

1 stringizing 字串化操作符 其作用是 將巨集定義中的傳入引數名轉換成用一對雙引號括起來引數名字串。其只能用於有傳入引數的巨集定義中,且必須置於巨集定義體中的引數名前。如 define example instr printf the input string is t s n inst...

define巨集定義中的 , , 及 符號

include stdio.h define size x printf 11s t 2d n x,sizeof x int main int argc,char argv 1 stringizing 字串化操作符。其作用是 將巨集定義中的傳入引數名轉換成用一對雙引號括起來引數名字串。其只能用於有傳...