青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-80  評論-24  文章-0  trackbacks-0

C(和C++)中的宏(Macro)屬于編譯器預處理的范疇,屬于編譯期概念(而非運行期概念)。下面對常遇到的宏的使用問題做了簡單總結(jié)。

關(guān) 于#和##

在C語言的宏中,#的功能是將其后面的宏參數(shù)進行字符串化操作(Stringfication),簡單說就是在對它所引用的宏 變量通過替換后在其左右各加上一個雙引號。比如下面代碼中的宏:

#define WARN_IF(EXP) \

do{ if (EXP) \

fprintf(stderr, "Warning: " #EXP "\n"); } \

while(0)

那么實際使用中會出現(xiàn)下面所示的替換過程:

WARN_IF (divider == 0);


被替換為


do {

if (divider == 0)

fprintf(stderr, "Warning" "divider == 0" "\n");

} while(0);

這樣每次divider(除數(shù))為0的時候便會在標 準錯誤流上輸出一個提示信息。

而## 被稱為連接符(concatenator),用來將兩個Token連接為一個Token。注意這里連接的對象是Token就行,而不一定是宏的變量。比如 你要做一個菜單項命令名和函數(shù)指針組成的結(jié)構(gòu)體的數(shù)組,并且希望在函數(shù)名和菜單項命令名之間有直觀的、名字上的關(guān)系。那么下面的代碼就非常實用:

struct command

{

char * name;

void (*function) (void);

};


#define COMMAND(NAME) { NAME, NAME ## _command }


// 然后你就用一些預先定義好的命令來方便的初始化一個command結(jié)構(gòu)的數(shù)組了:


struct command commands[] = {

COMMAND(quit),

COMMAND(help),

...

}

COMMAND宏在這里充當一個代碼生成器的作 用,這樣可以在一定程度上減少代碼密度,間接地也可以減少不留心所造成的錯誤。我們還可以n個##符號連接 n+1個Token,這個特性也是#符號所不具備的。比如:

#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d


typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);

// 這里這個語句將展開為:

// typedef struct _record_type name_company_position_salary;

關(guān) 于...的使用

...在C宏中稱為Variadic Macro,也就是變參宏。比如:

#define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)


// 或者


#define myprintf(templt,args...) fprintf(stderr,templt,args)

第 一個宏中由于沒有對變參起名,我們用默認的宏__VA_ARGS__來替代它。第二個宏中,我們顯式地命名變參為args,那么我們在宏定義中就可以用 args來代指變參了。同C語言的stdcall一樣,變參必須作為參數(shù)表的最有一項出現(xiàn)。當上面的宏中我們只能提供第一個參數(shù)templt時,C標準要 求我們必須寫成:

myprintf(templt,);

的形式。這時的替換過程為:

myprintf("Error!\n",);


替換為:



fprintf(stderr,"Error!\n",);

這是一個 語法錯誤,不能正常編譯。這個問題一般有兩個解決方法。首先,GNU CPP提供的解決方法允許上面的宏調(diào)用寫成:

myprintf(templt);

而 它將會被通過替換變成:

fprintf(stderr,"Error!\n",);

很明顯,這里仍然會產(chǎn)生編譯錯誤(非 本例的某些情況下不會產(chǎn)生編譯錯誤)。除了這種方式外,c99和GNU CPP都支持下面的宏定義方式:

#define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)

這 時,##這個連接符號充當?shù)淖饔镁褪钱擾_VAR_ARGS__為空的時候,消除前面的那個逗號。那么此時的翻譯過程如下:

myprintf(templt);


被轉(zhuǎn)化為:


fprintf(stderr,templt);

這樣如果templt合法,將不會產(chǎn)生 編譯錯誤。 這里列出了一些宏使用中容易出錯的地方,以及合適的使用方式。

錯誤的嵌套-Misnesting

宏的定義不一定要有完整的、配對的括號,但是為了避免出錯并且提高可讀性,最好避免這樣使用。

由 操作符優(yōu)先級引起的問題-Operator Precedence Problem

由于宏只是簡單的替換,宏的參數(shù)如果是復合結(jié)構(gòu),那么 通過替換之后可能由于各個參數(shù)之間的操作符優(yōu)先級高于單個參數(shù)內(nèi)部各部分之間相互作用的操作符優(yōu)先級,如果我們不用括號保護各個宏參數(shù),可能會產(chǎn)生預想不 到的情形。比如:

#define ceil_div(x, y) (x + y - 1) / y

那么

a = ceil_div( b & c, sizeof(int) );

將被轉(zhuǎn)化為:

a = ( b & c + sizeof(int) - 1) / sizeof(int);

// 由于+/-的優(yōu)先級高于&的優(yōu)先級,那么上面式子等同于:

a = ( b & (c + sizeof(int) - 1)) / sizeof(int);

這顯然不是調(diào)用者的初衷。為了避免這種情況發(fā)生,應(yīng)當多寫幾個括號:

#define ceil_div(x, y) (((x) + (y) - 1) / (y))

消除多余的分號-Semicolon Swallowing

通常情況下,為了使函數(shù)模樣的宏在表面上看起來像一個通常的C語言調(diào)用一樣,通常情況下我們在宏的后面加上一個分 號,比如下面的帶參宏:

MY_MACRO(x);

但是如果是下面的情況:

#define MY_MACRO(x) { \

/* line 1 */ \

/* line 2 */ \

/* line 3 */ }



//...


if (condition())

MY_MACRO(a);

else

{...}

這樣會由于多出的那個分號產(chǎn)生編譯錯誤。為了避免這種情況出現(xiàn)同時保持MY_MACRO(x);的這種寫法,我們 需要把宏定義為這種形式:

#define MY_MACRO(x) do {

/* line 1 */ \

/* line 2 */ \

/* line 3 */ } while(0)

這樣只要保證總是使用分號,就不會有任何問題。

Duplication of Side Effects

這里的Side Effect是指宏在展開的時候?qū)ζ鋮?shù)可能進行多次Evaluation(也就是取值),但是如果這個宏參數(shù)是一個函數(shù),那么就有可能被調(diào)用多次從而達 到不一致的結(jié)果,甚至會發(fā)生更嚴重的錯誤。比如:

#define min(X,Y) ((X) > (Y) ? (Y) : (X))


//...



c = min(a,foo(b));

這 時foo()函數(shù)就被調(diào)用了兩次。為了解決這個潛在的問題,我們應(yīng)當這樣寫min(X,Y)這個宏:

#define min(X,Y) ({ \

typeof (X) x_ = (X); \

typeof (Y) y_ = (Y); \

(x_ < y_) ? x_ : y_; })

({...})的作用是將內(nèi)部的幾條語句中最后一條的值返回,它也允許在內(nèi)部聲明變量(因為它通過大括號組成了一個局部 Scope)。

==

#define display(name) printf(""#name"") 
int main() { 
display(name); 

運行結(jié)果是name,為什么不是"#name"呢? 
--------------------------------------------------------------- 

#在這里是字符串化的意思 
printf(""#name"") 相當于 
printf("" "name" "") 
--------------------------------------------------------------- 

The number-sign or "stringizing" operator (#) converts macro parameters (after expansion) to string constants
--------------------------------------------------------------- 

printf("" #name "") <1> 
相當于printf("" "name" "") <2> 
而<2>中的第2,3個“中間時空格 等價于("空+name+空') 
--------------------------------------------------------------- 

## 連接符與# 符 

## 連接符號由兩個井號組成,其功能是在帶參數(shù)的宏定義中將兩個子串(token)聯(lián)接起來,從而形成一個新的子串。但它不可以是第一個或者最后一個子串。所 謂的子串 (token)就是指編譯器能夠識別的最小語法單元。具體的定義在編譯原理里有詳盡的解釋,但不知道也無所謂。同時值得注意的是#符是把傳遞過來的參數(shù)當 成字符串進行替代。下面來看看它們是怎樣工作的。這是MSDN上的一個例子。 

假設(shè)程序中已經(jīng)定義了這樣一個帶參數(shù)的宏: 
#define paster( n ) printf( "token" #n " = %d", token##n ) 

同時又定義了一個整形變 量: 
int token9 = 9; 

現(xiàn)在在主程序中以下面的方式調(diào)用這個宏: 
paster( 9 ); 

那 么在編譯時,上面的這句話被擴展為: 
printf( "token" "9" " = %d", token9 ); 

注意到 在這個例子中,paster(9);中的這個”9”被原封不動的當成了一個字符串,與”token”連接在了一起,從而成為了token9。而#n也 被”9”所替代。 

可想而知,上面程序運行的結(jié)果就是在屏幕上打印出token9=9 
--------------------------------------------------------------- 

#define display(name) printf(""#name"") 
int main() { 
display(name); 

==================================== 
特殊性就在 于它是個宏,宏里面處理#號就如LS所說! 
處理后就是一個附加的字符串! 

但printf(""#name"") ;就不行了! 
--------------------------------------------------------------- 

#define display(name) printf(""#name"") 

該定義 字符串化name, 
得 到結(jié)果其實就是 printf("name") 
(前后的空字符串拿掉) 

這樣輸出來的自然是 name 

從另 外一個角度講, 
#是一個連接符號, 
參與運算了, 自然不會輸出了 ...

posted on 2011-01-18 16:58 myjfm 閱讀(2366) 評論(0)  編輯 收藏 引用 所屬分類: c/c++基礎(chǔ)
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩中文字幕日韩欧美| 欧美一区视频| 亚洲欧美在线另类| 日韩视频在线一区| 亚洲伦伦在线| 国产欧美日韩麻豆91| 欧美精品一区视频| 欧美精品v日韩精品v韩国精品v| 国产精品毛片一区二区三区| 欧美日韩免费在线观看| 影音先锋亚洲电影| 亚洲女同在线| 欧美成人激情在线| 亚洲二区在线视频| 亚洲精选久久| 午夜欧美理论片| 欧美成人一区二区三区片免费 | 亚洲一区视频| 日韩午夜激情| 亚洲九九九在线观看| 老司机精品视频一区二区三区| 国产亚洲欧美激情| 亚洲电影av| 国产精品av免费在线观看| 久久久久久香蕉网| 性欧美在线看片a免费观看| 亚洲第一伊人| 欧美成人综合| 欧美gay视频激情| 每日更新成人在线视频| 国产精品国产三级国产普通话三级 | 欧美在线亚洲| 亚洲欧美国产高清| 一区二区三区 在线观看视频| 亚洲美女在线观看| 亚洲韩日在线| 午夜视频在线观看一区二区| 亚洲欧美激情诱惑| 久热re这里精品视频在线6| 久久婷婷国产综合尤物精品| 欧美大片专区| 亚洲欧美成人一区二区三区| 麻豆91精品91久久久的内涵| 欧美日韩国产免费| 蜜乳av另类精品一区二区| 欧美啪啪成人vr| 老牛嫩草一区二区三区日本| 亚洲午夜在线| 亚洲婷婷在线| 欧美午夜片在线观看| 亚洲欧洲一区二区三区在线观看| 免费不卡视频| 久久男人资源视频| 国产亚洲激情视频在线| 国产精品a久久久久| 欧美日韩岛国| 欧美成人激情视频| 亚洲国产成人91精品| 欧美国产精品中文字幕| 99视频精品免费观看| 亚洲欧美制服中文字幕| 国产精品伦一区| 午夜国产不卡在线观看视频| 亚洲国产精品99久久久久久久久| 久久久免费观看视频| 午夜视频在线观看一区二区| 蜜桃久久精品乱码一区二区| 精品成人免费| 亚洲欧美另类综合偷拍| 这里只有精品丝袜| 欧美日韩综合视频| 国产精品v日韩精品| 欧美国产另类| 欧美日韩一本到| 一本久久a久久精品亚洲| 国产视频自拍一区| 亚洲欧美日韩综合国产aⅴ| 最近中文字幕日韩精品| 欧美日韩国产在线播放| 亚洲激情一区| 91久久综合亚洲鲁鲁五月天| 欧美视频你懂的| 欧美一区二区三区四区在线| 91久久久久久久久| 久久综合中文| 香蕉久久夜色精品| 国产亚洲人成a一在线v站| 久久一区免费| 欧美日韩午夜| 亚洲视频图片小说| 亚洲欧美国产精品专区久久| 欧美日韩国产a| 美女爽到呻吟久久久久| 亚洲一区二区在线看| 在线亚洲免费| 久久综合久久久| 久久精品30| 亚洲综合999| 艳妇臀荡乳欲伦亚洲一区| 欧美在线亚洲在线| 99re视频这里只有精品| 久久精品人人做人人综合 | 午夜视频一区在线观看| 亚洲一区欧美激情| 欧美一区二区性| 亚洲制服av| 国产精品社区| 在线视频欧美日韩| 在线国产欧美| 久久久综合精品| 99这里只有精品| 欧美激情精品久久久久久蜜臀 | 亚洲高清网站| 久久久综合网站| 91久久精品网| 久久久久免费| 欧美午夜在线| 久久国产精品久久久久久| 欧美视频一区二区在线观看| 欧美亚洲视频一区二区| 国产色爱av资源综合区| 久久激情综合网| 一区二区三区黄色| 亚洲国产精品一区二区三区| 欧美激情综合色综合啪啪| 日韩一区二区精品| 久久久精品国产一区二区三区 | **性色生活片久久毛片| 宅男精品视频| 母乳一区在线观看| 欧美一区二区三区的| 夜夜嗨av色综合久久久综合网| 亚洲精品影院在线观看| 欧美日韩少妇| 欧美α欧美αv大片| 久久精品一区蜜桃臀影院| 国产亚洲欧美一级| 欧美美女操人视频| 久久9热精品视频| 午夜精品国产精品大乳美女| 欧美在线免费视屏| 亚洲精选在线| 亚洲欧洲在线播放| 久久黄色影院| 国产综合精品| 国产精品久久久久久久午夜| 亚洲福利视频网| 另类专区欧美制服同性| 亚洲欧美日韩在线| 一区二区三区高清| 一区二区国产在线观看| 在线中文字幕不卡| 免费在线一区二区| 久久精品视频导航| 亚洲高清av在线| 久久精品日韩欧美| 国产欧美在线看| 欧美一级二级三级蜜桃| 亚洲一区黄色| 欧美影院午夜播放| 美国成人直播| 一区二区欧美日韩视频| 韩国欧美一区| 亚洲国产美女精品久久久久∴| 一本色道久久综合亚洲精品高清| 欧美一区三区三区高中清蜜桃| 激情丁香综合| 欧美一区二区三区四区视频| 亚洲欧美一区二区视频| 国产一区高清视频| 欧美一级午夜免费电影| 一区二区不卡在线视频 午夜欧美不卡'| 久久性天堂网| 欧美私人网站| 欧美在线国产| 欧美精品黄色| 久久九九久久九九| 亚洲免费综合| 亚洲人成亚洲人成在线观看图片| 欧美激情精品久久久六区热门| 99国产精品| 久久大逼视频| 久久精品国产99精品国产亚洲性色| 老司机成人网| 久久精品系列| 午夜激情亚洲| 亚洲免费观看视频| 欧美一乱一性一交一视频| 国产精品99久久久久久久vr| 亚洲乱码精品一二三四区日韩在线 | 久久gogo国模啪啪人体图| 欧美电影电视剧在线观看| 有码中文亚洲精品| 欧美粗暴jizz性欧美20| 欧美日韩国产美女| 亚洲无玛一区| 久久九九精品99国产精品| 国产主播一区二区三区四区| 欧美一区二区三区免费观看视频| 亚洲欧美日韩一区二区三区在线 |