預處理器在原始碼編譯之前進行的一些文字性質的操作,它的主要任務包括刪除注釋,插入被#include指令包含的檔案內容,定義和替換由#define指令定義的符號以及確定**的部分內容是否應該根據一些條件編譯指令進行編譯。
__date__,字串常量型別,表示當前所在原始檔的編譯日期,輸出格式為mmm dd yyyy(如may 27 2006)。
__time__,字串常量型別,表示當前所在原始檔的編譯日期,輸出格式為hh:mm:ss(如09:11:10)。
__file__,字串常量型別,表示當前所在源檔名,且包含檔案路徑。
__line__,整數常量型別,表示當前所在原始檔中的行號。
__function__,字串常量型別,表示當前所在函式名。
__file__,進行編譯的原始檔。
預處理器在原始碼編譯之前進行的一些文字性質的操作,它的主要任務包括刪除注釋,插入被#include指令包含的檔案內容,定義和替換由#define指令定義的符號以及確定**的部分內容是否應該根據一些條件編譯指令進行編譯。
巨集定義的作用一般是用乙個短的名字代表乙個長的**序列。
巨集定義包括無引數巨集定義和帶引數巨集定義兩類。
巨集名和巨集引數所代表的**序列可以是任何意義的內容,如型別、常量、變數、操作符、表示式、語句、函式、**塊等。
但要尤其注意的是巨集名和巨集引數必須是合法的識別符號,其所代表的內容及意義在巨集展開前後必須一直是獨立且保持不變的,不能分開解釋和執行。
用乙個使用者指定的稱為巨集名的識別符號來代表乙個**序列,這種定義的一般形式為#define 識別符號 **序列。
其中#define之後的識別符號稱為巨集定義名(簡稱巨集名),在巨集定義#define之前可以有若干個空格、製表符,但不允許有其它字元,巨集名與**序列之間用空格符分隔。
形式為:
#define name stuff
帶引數巨集定義進一步擴充了無引數巨集定義的能力,這時的巨集展開既進行巨集名的替換又進行巨集引數的替換。帶引數的巨集定義的一般形式為
#define name(parameter-list) stuff
其中參數列中的引數之間用逗號分隔,在**序列中必須要包含參數列中的的引數。
在定義帶引數的巨集時,巨集名與左圓括號之間不允許有空白符,應緊接在一起,否則變成了無引數的巨集定義。
帶引數巨集呼叫提供的實參個數必須與巨集定義中的形式引數個數相同。
巨集定義的有效範圍稱為巨集名的作用域,巨集名的作用域從巨集定義的結束處開始到其所在的源**檔案末尾。巨集名的作用域不受分程式結構的影響。如果需要終止巨集名的作用域,可以用預處理指令#undef加上巨集名。
巨集名一般用大寫字母,以便與變數名區別。如有必要,巨集名可被重複定義,被重複定義後,巨集名原先的意義被新意義所代替。
注意:
巨集定義**序列中必須把""配對,不能把字串""拆開。例如#define name "vrmozart不合法,應為#define name "vrmozart"。
巨集定義**序列中可以引用已經定義的巨集名,即巨集定義可以巢狀。
巨集定義在原始檔中必須單獨另起一行,換行符是巨集定義的結束標誌,因此巨集定義以換行結束,不需要分號等符號作分隔符。
如果乙個巨集定義中**序列太長,一行不夠時,可採用續行的方法。續行是在鍵入回車符之前先鍵入符號\,注意回車要緊接在符號\之後,中間不能插入其它符號,當然**序列最後一行結束時不能有\。
注意多行巨集在呼叫時只能單獨一行呼叫,不能用在表示式中或作為函式引數。例如:
#define debug_print printf("file %s line %d:"\"x=%d,y=%d,z=%d,\
__file__,__line__,\
x,y,z)
預處理器在處理巨集定義時,會對巨集進行展開(即巨集替換)。
巨集替換首先將原始檔中在巨集定義隨後所有出現的巨集名均用其所代表的**序列替換之,如果是帶引數巨集則接著將**序列中的巨集形參名替換為巨集實參名。
巨集替換只作**字串行的替換工作,不作任何語法的檢查,也不作任何的中間計算,一切其它操作都要在替換完後才能進行。
如果巨集定義不當,錯誤要到預處理之後的編譯階段才能發現。
源**中的巨集名和巨集定義**序列中的巨集形參名必須是識別符號才會被替換,即只替換識別符號,不替換別的東西,像注釋、字串常量以及識別符號內出現的巨集名或巨集形參名則不會被替換。 如果希望巨集定義**序列中識別符號內出現的巨集形參名能夠被替換,可以在巨集形參名與識別符號之間新增連線符##,在巨集替換過程中巨集形參名和連線符##一起將被替換為巨集實參名。##用於把巨集引數名與巨集定義**序列中的識別符號連線在一起,形成乙個新的識別符號。例如:
#define blog(name) my_name_blog="name" //巨集定義**序列中的巨集形參名name也都不會被替換。#define blog(name) my_##name,blog(vrmozart)//表示my_vrmozart
如果希望巨集定義**序列中的巨集形參名被替換為巨集實參名的字串形式(即在巨集實參名兩端加雙引號"),而不是替換為巨集實參名,可以在巨集定義**序列中的巨集形參名前面新增符號#。#用於把巨集引數名變為乙個字串形式。例如:
#define str(name) #vrmozart,str(vrmozart)表示"vrmozart"
當巨集引數是另乙個巨集的時候,需要注意的是巨集定義**序列中有用#或##的巨集引數是不會再展開。
在巨集定義中說過,巨集名和巨集形參名所代表的內容及意義在巨集展開前後必須一直是獨立且保持不變的,不能分開解釋和執行。其原因如下,在巨集呼叫時,用巨集定義的**序列替換巨集名,用巨集實參名替換巨集形參名。替換後,巨集定義的**序列就與原始檔中相鄰的**自然連線,巨集實參名也與**序列中相鄰的**自然連線,巨集定義的**序列和巨集實參名的獨立性就不一定依舊存在。例如:
#define sqr(x) x*x,希望實現表示式的平方計算。
對於巨集呼叫p=sqr(y),能得到希望的巨集展開p=y*y。但對於巨集呼叫q=sqr(u+v),得到的巨集展開是q=u+v*u+v。顯然,後者的展開結果不是程式設計者所希望的。為能保持巨集實參名替換後的獨立性,應在巨集定義中給形式引數加上括號。進一步,為了保證巨集名呼叫的獨立性,作為算式的巨集定義**序列也應加括號。sqr巨集定義改寫成#define sqr(x) ((x)*(x))才是正確的巨集定義。
函式呼叫在程式執行時實行,而巨集展開是在編譯的預處理階段進行.
函式呼叫占用程式執行時間,巨集呼叫只佔編譯時間.
函式呼叫對實參有型別要求,而巨集呼叫實在引數與巨集定義形式引數之間沒有型別的概念,只有字串行的對應關係.
函式呼叫可返回乙個值,巨集呼叫獲得希望的**序列。
函式呼叫時,實參表示式分別獨立求值在前,執行函式體在後。巨集呼叫是實參字串行替換形式引數。
預定義,巨集定義
巨集定義 其作用就是 換其名曰 給程式中的一段特殊的 函式,資料取了個簡單明瞭的名字。不過有一點這傢伙的作用範圍是全域性的。即使它是在某乙個函式塊中定義的。這個其實也容易理解。因為巨集定義是預定義的一種,在我們的程式之前,由預編譯器 cpp 提前編譯出來了,那個時候程式裡的結構是個啥樣子編譯器壓根就...
預定義符號簡述
編譯乙個c程式涉及很多步驟,可概述為第一步 預處理 第二步 編譯 第三步 彙編 第四步 鏈結。其中,預處理又可分為巨集替換 標頭檔案包含 去注釋 條件編譯四個部分,該任務主要由c預處理器完成。下面,就來講述預處理器定義的符號。預定義符號有 file line data time stdc 這五種。f...
預定義 MSC VER巨集
一 介紹預定義巨集 msc ver 一.1 msc ver是微軟c c 編譯器 cl.exe編譯 時預定義的乙個巨集。需要針對cl編寫 時,可以使用該巨集進行條件編譯。一.2 msc ver的值表示cl的版本。需要針對cl特定版本編寫 時,也可以使用該巨集進行條件編譯。對應關係點此鏈結檢視。一.3 ...