程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 詳解C說話編程中預處置器的用法

詳解C說話編程中預處置器的用法

編輯:關於C++

詳解C說話編程中預處置器的用法。本站提示廣大學習愛好者:(詳解C說話編程中預處置器的用法)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解C說話編程中預處置器的用法正文


預處置最年夜的標記就是年夜寫,固然這不是尺度,但請你在應用的時刻年夜寫,為了本身,也為了先人。

預處置器在普通看來,用得最多的照樣宏,這裡總結一下預處置器的用法。

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

上述五個預處置是最常看見的,第一個代表著包括一個頭文件,可以懂得為沒有它許多功效都沒法應用,例如C說話並沒有把輸出輸出歸入尺度傍邊,而是應用庫函數來供給,所以只要包括了stdio.h這個頭文件,我們能力應用那些輸出輸入函數。 #define則是應用頻率第二高的預處置機制,普遍用在常量的界說,只不外它和const聲明的常量有所所差別:

#define MAR_VA 100
const 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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved