1. 程式人生 > >C預編譯指令 -- #define

C預編譯指令 -- #define

---------------------------------------------
--    2018-01-06  建立人:Ruo_Xiao
--    開發環境:VS2010
--    郵箱:[email protected]
--------------------------------------
--    2018-01-07  修改人:Ruo_Xiao
--    新增#undef指令
---------------------------------------------

一、概括

 1. 將一些文字替換成另外一些文字。
 2. 以“#”開始,第一個換行符為止。可以出現在原始檔任何位置。
 3. 有效範圍:從指令出現的地方到該檔案末尾或者該符號對應的#undef。
 4. ANSI之後,允許預編譯指令的“#”前面新增空格和製表符,“#”和指令的其餘部分之間亦可有空格。但是,之前是不允許的(K&R C)。
 5. 常用的預編譯指令:
 #define、#undef、#include、#ifdef、#else、#endif、#ifndef、#if、#elif、#line、#error和#pragma 。


 二、符號常量:#define

 #define        PI         3.1415926
 #define     Add(x,y)      ((x)+(y))
 
 //預編譯指令      巨集        替換體(替換列表)
 //X和Y被成為巨集引數。
 //注意:上述例子中每一個括號都是不可缺少的。

1、作用:一旦前處理器在程式中找到該巨集,就會用替換體替換該巨集。
2、巨集變成最終替換文字的過程被稱為巨集展開
3、巨集可以表示任何字串(數值、函式、字串)和其他巨集。
4、注意:巨集如果在雙引號裡面,預處理是不會進行文字替換的。
5、不帶引數的巨集 
(1)如上圖中定義的“PI”。
(2)有的編譯器將巨集的替換體看作是記號型字串,而不是傳統意義上的字元型字串。C前處理器記號是巨集定義的替換體中單獨的“詞”,用空格把這些詞分開。
例如:

#define  TWO  1*2

上述的巨集的替換體的記號為“1*2”,如果改成下例:

#define  TWO  1 * 2

則記號就有三個,分別是“1”、“*”和“2”。
小結:巨集的替換體解釋為字元型字串,則將空格作為替換體的一部分。解釋為記號型字串,則把空格作為替換體中各記號的分隔符。
(3)重定義常量
同一檔案中,同一符號被定義了兩次以上,這個過程被稱為重定義常量。例如:

#define   PI   3.1415
#define   PI   3.1415926

這種情況下,有的編譯器報錯,有的編譯器警告,有的編譯器沒有任何提示資訊(VS2010),以最後一個符號定義為準。
例如:

#define x  9
#define x  10

int iSum[x];

這裡iSum中元素有10個。

6、帶引數的巨集
(1)巨集中使用引數的外形和函式很像的被稱為類函式巨集。
(2)巨集函式和函式呼叫完全不同,前者是在預編譯期,將引數記號傳給了程式,後者是在程式執行期將引數的值傳給了函式,即不同的過程發生在了不同的時期。
(2)應用:

Z = Add(3,7);

7、用巨集引數建立字串:#運算子
(1)一般的巨集在字串中都會被視為普通的文字,但是允許在巨集替換中的字串中包含巨集引數。“#x”就可轉換為字串“x”的形參名,這個過程被稱為字串化(注意使用“”)。
(2)例如:

#include "stdafx.h"
#include <stdio.h>
#define POST(x)  printf("I am "#x",%d\n",((x)*(x)))

int _tmain(int argc, _TCHAR* argv[])
{
    POST(10);
    POST(2 + 3);

    getchar();
    return 0;
}

結果:
è¿éåå¾çæè¿°
8、前處理器的粘合劑,“##”指令
(1)將兩個記號組合成一個記號
(2)例如:

#include "stdafx.h"
#include <stdio.h>

#define XNAME(n)       iX##n  
#define Print_Xn(n)    printf("iX"#n" = %d\n",iX##n) 

int _tmain(int argc, _TCHAR* argv[])
{
    int XNAME(1) = 2;  //相當於:int iX1 = 2
    int XNAME(2) = 5;  //相當於:int iX2 = 5

    Print_Xn(1);
    Print_Xn(2);

    getchar();
    return 0;
}


結果:
è¿éåå¾çæè¿°
9、變參巨集
(1)指令如下:

"..." 和 "__VA_ARGS__"

這兩個指令組合可以讓使用者定義可變引數的類函式巨集。
(2)例如:

#include "stdafx.h"
#include <stdio.h>

#define Print_X(x,...)    printf("x = "#x","__VA_ARGS__) 

int _tmain(int argc, _TCHAR* argv[])
{
    int x = 11;
    int y = x*x;
    
    Print_X(x,"y = %d\n",y);

    getchar();
    return 0;
}

結果:

è¿éåå¾çæè¿°

10、巨集函式和普通函式的對比
(1)巨集定義只能寫在一行,雖然編譯器沒有硬性規定,但是請遵守這條約定。
(2)空間:使用巨集函式20個地方,就會有20個副本,但是函式只有一個。
(3)時間:巨集函式沒有函式跳轉,節省時間。如果巢狀呼叫,效果很明顯。
(4)巨集不用考慮形參的資料型別,因為在巨集眼裡,一切都是字串或者記號。


三、#undef

 1. 取消之前的#define定義。
 2. 即使之前沒有被定義,直接使用此指令也是沒有問題的。所以在使用某個符號時,若不確定之前是否已使用,可以在前面加上#undef。


四、拓展

字元常量和符號常量的區別
(1)字元常量:直接在程式碼中書寫的數值,例如:5.78、3.1415、7。
(2)符號常量:用一個識別符號代替一個常量。先定義後使用,例如:#define  PI  3.1415926

 

 

(SAW:Game Over!)