亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

詳解C語言編程中預處理器的用法

2020-01-26 14:43:47
字體:
來源:轉載
供稿:網友

預處理最大的標志便是大寫,雖然這不是標準,但請你在使用的時候大寫,為了自己,也為了后人。

預處理器在一般看來,用得最多的還是宏,這里總結一下預處理器的用法。

  • #include <stdio.h>
  • #define MACRO_OF_MINE
  • #ifdef MACRO_OF_MINE
  • #else
  • #endif

上述五個預處理是最??匆姷模谝粋€代表著包含一個頭文件,可以理解為沒有它很多功能都無法使用,例如C語言并沒有把輸入輸入納入標準當中,而是使用庫函數來提供,所以只有包含了stdio.h這個頭文件,我們才能使用那些輸入輸出函數。 #define則是使用頻率第二高的預處理機制,廣泛用在常量的定義,只不過它和const聲明的常量有所所區別:

#define MAR_VA 100const int Con_va = 100;.../*定義兩個數組*/...for(int i = 0;i < 10;++i){  mar_arr[i] = MAR_VA;  con_arr[i] = Con_va;}

區別1,定義上MAR_VA可以用于數組維數,而Con_va則不行
區別2,在使用時,MAR_VA的原理是在文中找到所有使用本身的地方,用值替代,也就是說Con_va將只有一分真跡,而MAR_VA則會有n份真跡(n為使用的次數) 剩下三個則是在保護頭文件中使用頗多。
幾個比較實用的用于調試的宏,由C語言自帶

  • __LINE__和__FILE__ 用于顯示當前行號和當前文件名
  • __DATE__和__TIME__ 用于顯示當前的日期和時間
  • __func__(C99) 用于顯示當前所在外層函數的名字

上述所說的五種宏直接當成值來使用即可。

__STDC__

如果你想檢驗你現在使用的編譯器是否遵循ISO標準,用它,如果是他的值為1。

printf("%d/n", __STDC__);

輸出: 1

如果你想進一步確定編譯器使用的標準版本是C99還是C89可以使用__STDC__VERSION__,C99(199901)

printf("%d/n", __STDC_VERSION__);

輸出: 199901

對于#define

預處理器一般只對同一行定義有效,但如果加上反斜杠,也能一直讀取下去

 #define err(flag) /   if(flag) /    printf("Correctly")

可以看出來,并沒有在末尾添加;,并不是因為宏不需要,而是因為,我們總是將宏近似當成函數在使用,而函數調用之后總是需要以;結尾,為了不造成混亂,于是在宏定義中我們默認不添加;,而在代碼源文件中使用,防止定義混亂。

預處理同樣能夠帶來一些便利

  #define SWAP1(a, b) (a += b, b = a - b, a -= b)  #define SWAP2(x, y) {x ^= y; y ^= x; x ^= y}

引用之前的例子,交換兩數的宏寫法可以有效避免函數開銷,由于其是直接在調用處展開代碼塊,故其比擬直接嵌入的代碼。但,偶爾還是會出現一些不和諧的錯誤,對于初學者來說:

 int v1 = 10; int v2 = 20; SWAP1(v1, v2); SWAP2(v1, v2);//報錯

對于上述代碼塊的情況,為什么SWAP2報錯?對于一般的初學者來說,經常忽略諸如, goto do...while等少見關鍵字用法,故很少見SWAP1的寫法,大多集中于SWAP2的類似錯誤,錯就錯在{}代表的是一個代碼塊,不需要使用;來進行結尾,這便是宏最容易出錯的地方 宏只是簡單的將代碼展開,而不會做任何處理 對于此,即便是老手也常有失足,有一種應用于單片機等地方的C語言寫法可以在此借鑒用于保護代碼:

 #define SWAP3(x ,y) do{ /     x ^= y; y ^= x; x ^= y; /     }while(0)

如此便能在代碼中安全使用花括號內的代碼了,并且如之前所約定的那樣,讓宏的使用看起來像函數。

但正所謂,假的總是假的,即使宏多么像函數,它依舊不是函數,如果真的把它當成函數,你會在某些時候錯的摸不著頭腦,還是一個經典的例子,比較大小:

 #define CMP(x, y) (x > y ? x : y) ... int x = 100, y = 200; int result = CMP(x, y++); printf("x = %d, y = %d, result = %d/n", x, y, result);

執行這部分代碼,會輸出什么呢? 答案是,不知道!至少result的值我們無法確定,我們將代碼展開得到

 int result = (x > y++ ? x : y++);

看起來似乎就是y遞增兩次,最后result肯定是200。真是如此?C語言標準對于一個確定的程序語句中,一個對象只能被修改一次,超過一次那么結果是未定的,由編譯器決定,除了三目操作符?:外,還有&&, ||或是,之中,或者函數參數調用,switch控制表達式,for里的控制語句 由此可看出,宏的使用也是有風險的,所以雖然宏強大,但是依舊不能濫用。

對于宏而言,前面說過,它只是進行簡單的展開,這有時候也會帶來一些問題:

 #define MULTI(x, y) (x * y) ... int x = 100, y = 200; int result = MULTI(x+y, y);

看出來問題了吧?展開之后會變成: int result = x+y * y; 完全違背了當初我們設計時的想法,一個比較好的修改方法是對每個參數加上括號: #define MULTI(x, y) ((x) * (y))如此,展開以后:

 int result = ((x+y) * (y));

這樣能在很大程度上解決一部分問題。

如果對自己的宏十分自信,可以嵌套宏,即一個表達式中使用宏作為宏的參數,但是宏只展開這一級的宏,對于多級宏另有辦法展開

 int result = MULTI(MULTI(x, y), y);

展開成:

int result = ((((x) * (y))) * (y));

對宏的應用

由于我們并不明白,在某些情況下宏是否被定義了,(NULL宏是一個例外,它可以被重復定義),所以我們可以使用一些預處理保護機制來防止錯誤發生

  •   #ifndef MY_MACRO
  •   #define MY_MACRO 10000
  •   #endif

如果定義了MY_MACRO那就不執行下面的語句,如果沒定義那就執行。

在宏的使用中有兩個有用的操作符,姑且叫它操作符#, ##

對于# 我們可以認為#操作符的作用是將宏參數轉化為字符串。

  #define HCMP(x, y) printf(#x" is equal to" #y" ? %d/n", (x) == (y))  ...  int x = 100, y = 200;  HCMP(x, y);

展開以后

 

  printf("x is equal to y ? %d/n", (100) == (200));

注:可以自行添加編譯器選項,來查看宏展開之后的代碼,具體可以查詢GCC的展開選項,這里不再詳述。特別是在多層宏的嵌套使用情況下,但是我不太推薦,故不做多介紹。

能說的就是如何正確的處理一些嵌套使用,之所以不愿意多說也不愿意多用,是因為C預處理器就是一個奇葩
舉一個典型的例子,__LINE__ 和 __FILE__的使用。

  /* 下方會說到的 # 預處理指示器,這里先用,實在看不懂,可以自己動手嘗試 */  #define WHERE_AM_I #__LINE__ " lines in " __FILE__  ...  fputs(WHERE_AM_I, stderr);

這樣能工作嗎?如果能我還講干嘛。

  /* 常理上這應該能工作,但是編譯器非說這錯那錯的 */  /* 好在有前人踏過了坑,為我們留下了解決方案 */  #define DEPAKEGE(X) #X  #define PAKEGE(X) DEPAKEGE(X)  #define WHERE_AM_I PAKEGE(__LINE__) " lines in " __FILE__  ...  fputs(WHERE_AM_I, stderr);

不要問我為什么,因為我也不知道C預處理器的真正工作機制是什么。

第一次看見這種解決方案是在 Windows 核心編程 中,這本書現在還能給我許多幫助,雖然已經漸漸淡出了書架

總結起來,即將宏參數放于#操作符之后便由預處理器自動轉換為字符串常量,轉義也由預處理器自動完成,而不需要我們自行添加轉義符號。

對于##
它實現的是將本操作符兩邊的參數合并成為一個完整的標記,但需要注意的是,由于預處理器只負責展開,所以程序員必須自己保證這種標記的合法性,這里涉及到一些寫法問題,都列出來

   #define MERGE(x, y) have_define_ ## (x + y)   #define MERGE(x, y) have_define_##(x + y)   ...   result = MERGE(1, 3);

這里首先說明,上述寫法由于習慣原因,我使用第二種,但是無論哪種都無傷大雅,效果一樣。上述代碼展開以后是什么呢?

     

result = have_define_1 + 3;

在我看來,這就有點C++中模版的思想了,雖然十分原始,但是總是有了一個方向,憑借這種方法我們能夠使用宏來進行相似卻不同函數的調用,雖然我們可以使用函數指針數組來存儲,但需要提前知曉有幾個函數,并且如果要實現動態增長還需要消耗內存分配,但宏則不同。

   inline int func_0(int arg_1, int arg_2) { return arg_1 + arg_2; }   inline int func_1(int arg_1, int arg_2) { return arg_1 - arg_2; }   inline int func_2(int arg_1, int arg_2) { return arg_1 * arg_2; }   inline int func_3(int arg_1, int arg_2) { return arg_1 / arg_2; }   #define CALL(x, arg1, arg2) func_##x(arg1, arg2)   ...     printf("func_%d return %d/n",0 ,CALL(0, 2, 10));     printf("func_%d return %d/n",1 ,CALL(1, 2, 10));     printf("func_%d return %d/n",2 ,CALL(2, 2, 10));     printf("func_%d return %d/n",3 ,CALL(3, 2, 10));

十分簡便的一種用法,在我們增加減少函數時我們不必考慮如何找到這些函數只需要記下每個函數對應的編號即可,但還是那句話,不可濫用。

   #define CAT(temp, i) (cat##i)   //...   for(int i = 0;i < 5;++i)   {     int CAT(x,i) = i*i;     printf("x%d = %d /n",i,CAT(x,i));   }

對于宏,在使用時一定要注意,宏只能展開當前層的宏,如果你嵌套使用宏,即將宏當作宏的參數,那么將導致宏無法完全展開,即作為參數的宏只能傳遞名字給外部宏

 

  #define WHERE(value_name, line) #value_name #line  ...  puts(WHERE(x, __LINE__)); //x = 11

輸出: 11__LINE__

對于其他的預編譯器指令,如:#program, #line, #error和各類條件編譯并不在此涉及,因為使用上并未有陷阱及難點。

C和C++混合編程的情況

經常能在源代碼中看見 extern "C" 這樣的身影,這是做什么的?
這是為了混合編程而設計的,常出現在 C++的源代碼中,目的是為了讓 C++能夠成功的調用 C 的標準或非標準函數。

  #if defined(__cplusplus) || defined(_cplusplus)      extern "C" {  #endif      /**主體代碼**/  #if defined(__cplusplus) || defined(_cplusplus)      }  #endif

這樣就能在C++中調用C的代碼了。

在 C 中調用 C++ 的函數需要注意,不能使用重載功能,否則會失敗,原因詳見C++對于重載函數的實現。也可以稱為 mangle

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美成人亚洲成人日韩成人| 国产日韩欧美视频| 欧美视频在线观看免费网址| 亚洲天堂av在线播放| 这里只有视频精品| 精品国产网站地址| 日韩网站在线观看| 中文字幕国产精品久久| 亚洲午夜久久久影院| 日韩av电影中文字幕| 精品久久香蕉国产线看观看gif| 亚洲精品美女久久久| 欧美乱人伦中文字幕在线| 成人h猎奇视频网站| 久久亚洲国产成人| 亚洲精品日韩丝袜精品| 欧美黄色免费网站| 国产亚洲美女久久| 久久久久北条麻妃免费看| 国产精品成人一区二区| 亚洲国产精品va在线看黑人动漫| 亚洲开心激情网| 国产精品视频久| 亚洲性生活视频| 日韩电影在线观看免费| 日韩欧美在线视频观看| 久久久久国产精品免费| 深夜福利亚洲导航| 宅男66日本亚洲欧美视频| 欧美日韩国产在线播放| 国内精品久久久久久中文字幕| 国产日韩中文在线| 不卡中文字幕av| 91欧美精品午夜性色福利在线| 亚洲国产女人aaa毛片在线| 欧美激情一区二区三区高清视频| 国产精品久久91| 国产激情久久久| 国产精品视频精品视频| 亚洲人成电影网站| 国产一区二区三区日韩欧美| 中文字幕在线看视频国产欧美在线看完整| 欧美日韩亚洲精品内裤| 欧美激情小视频| 中文国产成人精品久久一| 久久久久国产精品免费网站| 亚洲视频在线观看| 国产精品国产自产拍高清av水多| 亚洲精品国产精品国自产在线| 国产中文日韩欧美| 黑人狂躁日本妞一区二区三区| 国产精品18久久久久久麻辣| 夜夜嗨av色一区二区不卡| 国产欧美一区二区三区久久| 成人久久久久爱| 国产精品久久久久秋霞鲁丝| 亚洲影影院av| 久久久久99精品久久久久| 国产综合福利在线| 国产丝袜一区视频在线观看| 成人在线视频网站| 亚洲资源在线看| 在线看日韩av| 午夜剧场成人观在线视频免费观看| 国产视频精品在线| 亚洲自拍偷拍福利| 日韩欧美国产中文字幕| 久久乐国产精品| 91免费视频网站| 在线观看精品自拍私拍| xxav国产精品美女主播| 欧美性猛交xxxxx水多| 国产精品一区二区性色av| 欧美激情一级欧美精品| 欧美在线亚洲一区| 日韩精品视频免费专区在线播放| 欧美国产欧美亚洲国产日韩mv天天看完整| 国产在线精品自拍| 中文字幕亚洲一区在线观看| 亚洲第一精品夜夜躁人人爽| 久久激情视频免费观看| 久久久久中文字幕2018| 亚洲xxxx3d| 国产精品大陆在线观看| 日韩中文字幕视频| 欧美激情综合色综合啪啪五月| 97精品国产97久久久久久春色| 中文字幕精品在线视频| 中国china体内裑精亚洲片| 久久亚洲精品视频| 国产精品日韩电影| 久久久影视精品| 亚洲日韩欧美视频一区| 国产99久久精品一区二区| 欧美www视频在线观看| 欧美日韩亚洲91| 日韩欧美aⅴ综合网站发布| 97色在线播放视频| 久久久精品中文字幕| 亚洲欧美日韩一区二区在线| 亚洲精品电影在线| 日韩在线视频免费观看| 欧美一区二粉嫩精品国产一线天| 51色欧美片视频在线观看| 日韩av免费在线观看| 91av在线不卡| 亚洲人精选亚洲人成在线| 欧美性生交xxxxx久久久| 国产成人精品免费久久久久| 欧美激情精品久久久久久久变态| 欧美午夜影院在线视频| 欧美激情2020午夜免费观看| 久青草国产97香蕉在线视频| 欧美一级片久久久久久久| 国产91对白在线播放| 国产精品视频一区二区三区四| 国产一区二区三区中文| 91国在线精品国内播放| 久久久久国产精品免费网站| 2019中文字幕在线| 精品久久久久久中文字幕一区奶水| 日韩在线免费观看视频| 久久69精品久久久久久久电影好| 色偷偷噜噜噜亚洲男人| 欧美日韩国产一中文字不卡| 在线成人中文字幕| 欧美一区在线直播| 美女国内精品自产拍在线播放| 欧美专区在线播放| 欧美性生活大片免费观看网址| 精品久久久久久中文字幕| 日韩av影院在线观看| 国产精品丝袜白浆摸在线| 91精品国产高清自在线看超| 97人人模人人爽人人喊中文字| 欧美专区第一页| 精品久久久久久| 亚洲视频在线观看| 国产成人综合一区二区三区| 欧美亚洲成人精品| 久久精品国产免费观看| 久久影院在线观看| 日韩电影中文字幕一区| 色综合天天狠天天透天天伊人| 久久久久九九九九| 国产精品视频免费在线| www日韩中文字幕在线看| 欧美成人黑人xx视频免费观看| 精品久久久久久久久国产字幕| 亚洲xxxx视频| 性夜试看影院91社区| 日韩小视频网址| 国产91精品久久久久久| 精品国偷自产在线视频99| 亚洲精品国产精品国产自| 国产xxx69麻豆国语对白| 亚洲男人7777| 91久热免费在线视频| 亚洲有声小说3d| 欧洲成人在线视频| 红桃av永久久久| 狠狠做深爱婷婷久久综合一区| 日韩精品丝袜在线| 成人欧美在线观看|