1. 程式人生 > >C++ 中常見預定義巨集的使用【轉】

C++ 中常見預定義巨集的使用【轉】

(轉自:https://blog.csdn.net/hgl868/article/details/7058906)

在標準C以及各中編譯器中定義了一些物件巨集, 這些巨集的名稱以"__"開頭和結尾, 並且都是大寫字元. 這些預定義巨集可以被#undef, 也可以被重定義。

      在ANSI C標準中定義了__FILE__,__LINE__,__DATA__,__TIME__,__STDC__等標準的預定義巨集。GCC對其進行擴充套件,也定義了多個預定義巨集。

      概括起來GCC中可使用的預定義巨集涵蓋了如下幾方面的資訊:
      1、宿主的資訊:GNU的版本,編譯器的版本,型別的相關資訊,位元組序資訊等。
      2、編譯動作的資訊:編譯的日期、時間;編譯時是否進行了時間或空間上的優化;定義的inline是否被編譯器執行等。
      3、檔案的資訊:檔名稱、函式名稱、行數資訊、檔案最後修改時間等等。
      4、計數資訊:__COUNTER__,__INCLUDE_LEVEL__等。

      下面是一些常見的預定義巨集的使用方法。

1、__FILE__,__LINE__,FUNCTION__
      這是最常用到的預定義巨集的組合,表示檔名、行數和函式名,用於程式執行期異常的跟蹤。如:

//-------file main.c----------
#include <stdio.h>
#include "myassert.h"

int func(const char *filename);

int main(int argc,char **argv)
{
    MyAssert("two args are needed",argc==2);
    func(argv[1]);
    return 0;
}


//-------file func.c----------
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "myassert.h"

int func(const char *filename)
{
    int fd;
    MyAssert("filename can not be null",filename);
    MyAssert("file not exist",0==access(filename,F_OK));
    fd = open(filename,O_RDONLY);
    close(fd);
    return 0;
}


//-------file myassert.h----------
#ifndef __MY_ASSERT_H__
#define __MY_ASSERT_H__

#include <stdio.h>
#include <stdlib.h>

#define  MyAssert(message,assertion) do{/
    if(!(assertion)){/
        printf("line %d in %s(%s)", __LINE__, __FILE__,__FUNCTION__);/
        if(message){/
            printf(" : %s",message);/
        }/
        printf("/n");/
        abort();/
    }/
}while(0);
#endif

#Makefile
TARGET = test
CC = gcc
CCFLAGS = -Wall
OBJS = main.o func.o

$(TARGET) : $(OBJS)
    $(CC) -o

[email protected] $(OBJS) $(CCFLAGS)

%.o : %.c
    $(CC) -o [email protected] -c $< $(CCFLAGS)

clean:
    rm -rf *.o
    rm -rf $(TARGET)

      可見通過使用__FILE__,__LINE__,FUNCTION__巨集,可以幫助我們精確的定位出現異常的檔案、函式和行數。

2、__BASE_FILE__

      這個巨集是和__FILE__相對應的,表示主輸入檔案的名字,對於原始檔而言__FILE__和__BASE_FILE__是一樣的;對於標頭檔案二者才可能不同。比如在上個例子中,__LINE__這個巨集是在myassert.h檔案中定義的,被main.c和func.c包含之後__FILE__的值
分別變成了main.c和func.c。但是當我們希望知道MyAssert這個巨集具體實在哪個檔案(實際上是myassert.h)中定義的話,就需要用到__BASE_FILE__。
     下面的例子可以幫助加深理解:

 //-------file main.c----------
#include <stdio.h>
#include "basefile.h"

int main(int argc, char *argv[])
{
    printf("%s/n",sfile);
    printf("%s/n",hfile);
    return 0;
}

//-------file basefile.h----------
const char sfile[]= __FILE__;
const char hfile[]= __BASE_FILE__;

       gcc main.c &&./a.out 得到:

       basefile.h
       main.c

 3、__DATE__,__TIME__
       用於得到最後一次編譯的日期和時間(字串形式):

#include <stdio.h>
//-------file main.c----------
int main()
{
    printf("DATE : %s/n",__DATE__);
    printf("TIME : %s/n",__TIME__);
}

       gcc main.c &&./a.out 得到:
       DATE : Jan 27 2011
       TIME : 17:12:55

4、__TIMESTAMP__
       和__TIME__的格式相同。同於得到本檔案最後一次被修改的時間。

5、__GNUC__、__GNUC_MINOR__、__GNUC_MINOR__、__GNUC_PATCHLEVEL__
       用於得到GNU版本:

#include <stdio.h>
int main()
{
    if( __GNUC__ > 4 ||
        (__GNUC__ == 4 && (__GNUC_MINOR__ > 2 ||  
            (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ > 0)))){
        printf("GNUC version is later than 3.3.2/n");
    }else{
        printf("GNUC version is older than 3.3.2/n");
    }

}


6、__VERSION__
     用於得到編譯器的版本      

//-------file main.c----------
#include <stdio.h>
int main()
{
    printf("Version : %s/n",__VERSION__);
    return 0;
}

      gcc main.c && ./a.out得到:

      Version : 4.1.2 (Gentoo 4.1.2 p1.0.2)
      可以和gcc -v相互驗證


7、__COUNTER__
      自身計數器,用於記錄以前編譯過程中出現的__COUNTER__的次數,從0開始計數。常用於構造一系列的變數名稱,函式名稱等。如:
//-------file main.c----------
#include <stdio.h>

#define FUNC2(x,y) x##y
#define FUNC1(x,y) FUNC2(x,y)  
#define FUNC(x) FUNC1(x,__COUNTER__)

int FUNC(var);
int FUNC(var);

int main() {
    var0 = 0;
    var1 = 1;
    printf("%d/n",var0);
    printf("%d/n",var1);
    return 0;
}
      gcc main.c &&a.out得到結果:
      0
      1
      這裡使用__COUNTER__構造了兩個變數:var0,var1。

8、__INCLUDE_LEVEL__
      用於表示檔案被包含的計數,從0開始遞增,常作為遞迴包含的限制條件。如:

 
//-------file main.c----------
#include <stdio.h>
int main()
{
   #define REP_LIMIT 10
   #define REP(BLAH) printf("%d ", BLAH);
   #include "rep.h"
   printf("/n");
   return 0;
}

//--------file rep.h----------
#if __INCLUDE_LEVEL__ < REP_LIMIT
REP(__INCLUDE_LEVEL__)
#include "rep.h"
#endif

      gcc main.c && ./a.out,得到結果:
      1 2 3 4 5 6 7 8 9
      在這個例子中檔案rep.h自包含了9次,執行了9次REP(BLAH)。

      實際上,__INCLUDE_LEVEL__最多的是和#include __FILE__組合使用,用於表示一個遞迴。如:

 //-------file main.c----------
#ifndef AUTOINC
#define AUTOINC

#include <stdio.h>
#define MAX_LEVEL 10
int main()
{
    int i = 0;
    #include __FILE__
    printf("/n");
    return 0;
}
#undef AUTOINC
#endif

#ifdef AUTOINC
    #if __INCLUDE_LEVEL__ <= MAX_LEVEL
    printf("%d ",__INCLUDE_LEVEL__);
    #include __FILE__

    #if __INCLUDE_LEVEL__ != MAX_LEVEL
    printf("%d ",__INCLUDE_LEVEL__);
    #endif

    #endif
#endif
      gcc main.c && ./a.out得到結果:
      1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1

ANSI C標準中有幾個標準預定義巨集:

__FILE__     __DATE__   __TIME___    __LINE__   等

__LINE__在原始碼中插入當前原始碼行號;

__FILE__在原始檔中插入當前原始檔名;

__DATE__在原始檔中插入當前的編譯日期

__TIME__在原始檔中插入當前編譯時間;

__STDC__當要求程式嚴格遵循ANSI C標準時該標識被賦值為1;

__cplusplus:當編寫C++程式時該識別符號被定義。


一、標準預定義巨集
The standard predefined macros are specified by the relevant language standards, so they are available with all compilers that implement those standards. Older compilers may not provide all of them. Their names all start with double underscores.

__FILE__
This macro expands to the name of the current input file, in the form of a C string constant. This is the path by which the preprocessor opened the file, not the short name specified in #include or as the input file name argument. For example, "/usr/local/include/myheader.h" is a possible expansion of this macro.

__LINE__
This macro expands to the current input line number, in the form of a decimal integer constant. While we call it a predefined macro, it's a pretty strange macro, since its "definition" changes with each new line of source code.
__FILE__ and __LINE__ are useful in generating an error message to report an inconsistency detected by the program; the message can state the source line at which the inconsistency was detected. For example,

     fprintf (stderr, "Internal error: "
                      "negative string length "
                      "%d at %s, line %d.",
              length, __FILE__, __LINE__);
    
An #include directive changes the expansions of __FILE__ and __LINE__ to correspond to the included file. At the end of that file, when processing resumes on the input file that contained the #include directive, the expansions of __FILE__ and __LINE__ revert to the values they had before the #include (but __LINE__ is then incremented by one as processing moves to the line after the #include).

A #line directive changes __LINE__, and may change __FILE__ as well. See Line Control.

C99 introduces __func__, and GCC has provided __FUNCTION__ for a long time. Both of these are strings containing the name of the current function (there are slight semantic differences; see the GCC manual). Neither of them is a macro; the preprocessor does not know the name of the current function. They tend to be useful in conjunction with __FILE__ and __LINE__, though.


__DATE__
This macro expands to a string constant that describes the date on which the preprocessor is being run. The string constant contains eleven characters and looks like "Feb 12 1996". If the day of the month is less than 10, it is padded with a space on the left.
If GCC cannot determine the current date, it will emit a warning message (once per compilation) and __DATE__ will expand to "??? ?? ????".


__TIME__
This macro expands to a string constant that describes the time at which the preprocessor is being run. The string constant contains eight characters and looks like "23:59:01".
If GCC cannot determine the current time, it will emit a warning message (once per compilation) and __TIME__ will expand to "??:??:??".


__STDC__
In normal operation, this macro expands to the constant 1, to signify that this compiler conforms to ISO Standard C. If GNU CPP is used with a compiler other than GCC, this is not necessarily true; however, the preprocessor always conforms to the standard unless the -traditional-cpp option is used.
This macro is not defined if the -traditional-cpp option is used.

On some hosts, the system compiler uses a different convention, where __STDC__ is normally 0, but is 1 if the user specifies strict conformance to the C Standard. CPP follows the host convention when processing system header files, but when processing user files __STDC__ is always 1. This has been reported to cause problems; for instance, some versions of Solaris provide X Windows headers that expect __STDC__ to be either undefined or 1. See Invocation.


__STDC_VERSION__
This macro expands to the C Standard's version number, a long integer constant of the form yyyymmL where yyyy and mm are the year and month of the Standard version. This signifies which version of the C Standard the compiler conforms to. Like __STDC__, this is not necessarily accurate for the entire implementation, unless GNU CPP is being used with GCC.
The value 199409L signifies the 1989 C standard as amended in 1994, which is the current default; the value 199901L signifies the 1999 revision of the C standard. Support for the 1999 revision is not yet complete.

This macro is not defined if the -traditional-cpp option is used, nor when compiling C++ or Objective-C.


__STDC_HOSTED__
This macro is defined, with value 1, if the compiler's target is a hosted environment. A hosted environment has the complete facilities of the standard C library available.

__cplusplus
This macro is defined when the C++ compiler is in use. You can use __cplusplus to test whether a header is compiled by a C compiler or a C++ compiler. This macro is similar to __STDC_VERSION__, in that it expands to a version number. A fully conforming implementation of the 1998 C++ standard will define this macro to 199711L. The GNU C++ compiler is not yet fully conforming, so it uses 1 instead. We hope to complete our implementation in the near future.

__OBJC__
This macro is defined, with value 1, when the Objective-C compiler is in use. You can use __OBJC__ to test whether a header is compiled by a C compiler or a Objective-C compiler.

__ASSEMBLER__
This macro is defined with value 1 when preprocessing assembly language.


__DATE__ 進行預處理的日期(“Mmm dd yyyy”形式的字串文字,如May 27 2006)
__FILE__ 代表當前原始碼檔名的字串文字 ,包含了詳細路徑,如G:/program/study/c+/test1.c
__LINE__ 代表當前原始碼中的行號的整數常量
__TIME__ 原始檔編譯時間,格式微“hh:mm:ss”,如:09:11:10;
__func__ 當前所在函式名,在編譯器的較高版本中支援
__FUNCTION__ 當前所在函式名

  對於__FILE__,__LINE__,__func__,__FUNCTION__ 這樣的巨集,在除錯程式時是很有用的,因為你可以很容易的知道程式執行到了哪個檔案的那一行,是哪個函式。
而對於__DATE__,__TIME__則可以獲取編譯時間,如如下程式碼通過巨集獲取編譯時間,並通過sscanf()從中獲取具體的年月日時分秒資料,可在程式碼中做相應使用。我的程式碼中是根據此資料作為版本標識,並依此判斷哪個版本新些及是否需要升級。
char * creationDate   = __DATE__ ", " __TIME__;
sscanf(creationDate, "%s %d %d, %d:%d:%d", month, &day, &year, &hour, &min, &sec);

預處理命令#pragma和預定義巨集--轉載

一、C預定義巨集
C標準指定了一些預定義巨集,程式設計中常常用到。
__DATE__     進行預處理的日期
__FILE__     代表當前原始碼檔名的字串
__LINE__     代表當前原始碼檔案中行號的整數常量
__STDC__     設定為1時,表示該實現遵循C標準
__STDC_HOSTED__  為本機環境設定為,否則設為0
__STDC_VERSION__ 為C99時設定為199901L
__TIME__     原始檔的編譯時間
__func__     C99提供的,為所在函式名的字串
對於__FILE__,__LINE__,__func__這樣的巨集,在除錯程式時是很有用的,因為你可以很容易的知道程式執行到了哪個檔案的那一行,是哪個函式.

例如:
#include
#include
void why_me();
int main()
{
    printf( "The file is %s/n", __FILE__ );
    printf( "The date is %s/n", __DATE__ );
    printf( "The time is %s/n", __TIME__ );
    printf("The version is %s/n",__STDC__VERSION__);
    printf( "This is line %d/n", __LINE__ );
    printf( "This function is %s/n ", __func__ );
   why_me();
   return 0;
}
void why_me()
{
    printf( "This function is %s/n", __func__ );
    printf( "This is line %d/n", __LINE__ );
}

二、#line和#error
#line用於重置由__LINE__和__FILE__巨集指定的行號和檔名。
用法如下:#line number filename
例如:#line 1000 //將當前行號設定為1000
     #line 1000 "lukas.c"   //行號設定為1000,檔名設定為lukas.c

#error指令使前處理器發出一條錯誤訊息,該訊息包含指令中的文字.這條指令的目的就是在程式崩潰之前能夠給出一定的資訊。

三、#pragma
在所有的預處理指令中,#Pragma 指令可能是最複雜的了。#pragma的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C++語言完全相容的情況下,給出主機或作業系統專有的特徵。依據定義,編譯指示是機器或作業系統專有的,且對於每個編譯器都是不同的。
其格式一般為: #Pragma Para
其中Para 為引數,下面來看一些常用的引數。

(1)message 引數。 Message 引數是我最喜歡的一個引數,它能夠在編譯資訊輸出視窗中輸出相應的資訊,這對於原始碼資訊的控制是非常重要的。其使用方法為:
#Pragma message(“訊息文字”)
當編譯器遇到這條指令時就在編譯輸出視窗中將訊息文字打印出來。
當我們在程式中定義了許多巨集來控制原始碼版本的時候,我們自己有可能都會忘記有沒有正確的設定這些巨集,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在原始碼的什麼地方定義了_X86這個巨集可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
當我們定義了_X86這個巨集以後,應用程式在編譯時就會在編譯輸出窗口裡顯示“_
X86 macro activated!”。我們就不會因為不記得自己定義的一些特定的巨集而抓耳撓腮了。

(2)另一個使用得比較多的pragma引數是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能夠設定程式中函式程式碼存放的程式碼段,當我們開發驅動程式的時候就會使用到它。

(3)#pragma once (比較常用)
只要在標頭檔案的最開始加入這條指令就能夠保證標頭檔案被編譯一次。這條指令實際上在VC6中就已經有了,但是考慮到相容性並沒有太多的使用它。

(4)#pragma hdrstop表示預編譯標頭檔案到此為止,後面的標頭檔案不進行預編譯。BCB可以預編譯標頭檔案以加快連結的速度,但如果所有標頭檔案都進行預編譯又可能佔太多磁碟空間,所以使用這個選項排除一些標頭檔案。
有時單元之間有依賴關係,比如單元A依賴單元B,所以單元B要先於單元A編譯。你可以用#pragma startup指定編譯優先順序,如果使用了#pragma package(smart_init) ,BCB就會根據優先順序的大小先後編譯。

(5)#pragma resource "*.dfm"表示把*.dfm檔案中的資源加入工程。*.dfm中包括窗體外觀的定義。

(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )等價於:
#pragma warning(disable:4507 34) /* 不顯示4507和34號警告資訊。如果編譯時總是出現4507號警告和34號警告,
                                    而認為肯定不會有錯誤,可以使用這條指令。*/
#pragma warning(once:4385) // 4385號警告資訊僅報告一次
#pragma warning(error:164) // 把164號警告資訊作為一個錯誤。
同時這個pragma warning 也支援如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這裡n代表一個警告等級(1---4)。
#pragma warning( push )儲存所有警告資訊的現有的警告狀態。
#pragma warning( push, n)儲存所有警告資訊的現有的警告狀態,並且把全域性警告等級設定為n。
#pragma warning( pop )向棧中彈出最後一個警告資訊,在入棧和出棧之間所作的一切改動取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在這段程式碼的最後,重新儲存所有的警告資訊(包括4705,4706和4707)。

(7)pragma comment(...)
該指令將一個註釋記錄放入一個物件檔案或可執行檔案中。
常用的lib關鍵字,可以幫我們連入一個庫檔案。 

(8)progma pack(n)
    指定結構體對齊方式!#pragma pack(n)來設定變數以n位元組對齊方式。n 位元組對齊就是說變數存放的起始地址的偏移量有兩種情況:第一、如果n大於等於該變數所佔用的位元組數,那麼偏移量必須滿足預設的對齊方式,第二、如果n小於該變數的型別所佔用的位元組數,那麼偏移量為n的倍數,不用滿足預設的對齊方式。結構的總大小也有個約束條件,分下面兩種情況:如果n大於所有成員變數型別所佔用的位元組數,那麼結構的總大小必須為佔用空間最大的變數佔用的空間數的倍數; 否則必須為n的倍數。下面舉例說明其用法。

#pragma pack(push) //儲存對齊狀態
#pragma pack(4)//設定為4位元組對齊
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢復對齊狀態
為測試該功能,可以使用sizeof()測試結構體的長度!


在你寫dll的時候,因為對於C和C++,編譯器會有不同的名字解析規則,所以可以這樣用

#ifndef   __STDC__

extern "C "   void   function();

#else

void   function();

#endif

 

 __LINE__           在原始碼中插入當前原始碼行號
  __FILE__           在原始碼中插入當前原始碼檔名
  __DATE__           在原始碼中插入當前編譯日期〔注意和當前系統日期區別開來〕
  __TIME__           在原始碼中插入當前編譯時間〔注意和當前系統時間區別開來〕  
  __STDC__           當要求程式嚴格遵循ANSIC標準時該識別符號被賦值為1。
----------------------------------------------------------------------------

識別符號__LINE__和__FILE__通常用來除錯程式;識別符號__DATE__和__TIME__通常用來在編譯後的程式中加入一個時間標誌,以區分程式的不同版本;當要求程式嚴格遵循ANSIC標準時,識別符號__STDC__就會被賦值為1;當用C++編譯程式編譯時,識別符號__cplusplus就會被定義。

#include

 

int main ()

{

    printf("該輸出行在源程式中的位置:%d/n", __LINE__ );

    printf("該程式的檔名為:%s/n", __FILE__ );

    printf("當前日期為:%s/n", __DATE__ );

    printf("當前時間為:%s/n", __TIME__ );

 

    return 0;

}

#include

void main(void)

{

    printf("%d",__LINE__); // Line 5

}

結果為:5

 

// 標準預定義巨集巨集.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include

void main(void)

{
 
    printf("%d",__LINE__); // Line 5
 

 

}

 

編譯器巨集使用總結
 
 
 

C/C++中巨集總結C程式的原始碼中可包括各種編譯指令,這些指令稱為預處理命令。雖然它們實際上不是C語言的一部分,但卻擴充套件了C程式設計的環境。本節將介紹如何應用預處理程式和註釋簡化程式開發過程,並提高程式的可讀性。ANSI標準定義的C語言預處理程式包括下列命令:

 

#define,#error,#include,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。非常明顯,所有預處理命令均以符號#開頭,下面分別加以介紹。

 

命令#define定義了一個識別符號及一個串。在源程式中每次遇到該識別符號時,均以定義的串代換它。ANSI標準將識別符號定義為巨集名,將替換過程稱為巨集替換。命令的一般形式為:

 

#define identifier string

 

注意:

? 該語句沒有分號。在識別符號和串之間可以有任意個空格,串一旦開始,僅由一新行結束。

? 巨集名定義後,即可成為其它巨集名定義中的一部分。

? 巨集替換僅僅是以文字串代替巨集識別符號,前提是巨集識別符號必須獨立的識別出來,否則不進行替換。例如:

#define XYZ this is a tes

使用巨集printf("XYZ");//該段不列印"this is a test"而列印"XYZ"。因為預編譯器識別出的是"XYZ"

? 如果串長於一行,可以在該行末尾用一反斜槓' /'續行。

 

處理器命令#error強迫編譯程式停止編譯,主要用於程式除錯。

 

#include 命令#i nclude使編譯程式將另一原始檔嵌入帶有#i nclude的原始檔,被讀入的原始檔必須用雙引號或尖括號括起來。例如:

 

#i nclude"stdio.h"或者#i nclude

 

這兩行程式碼均使用C編譯程式讀入並編譯用於處理磁碟檔案庫的子程式。

將檔案嵌入#i nclude命令中的檔案內是可行的,這種方式稱為巢狀的嵌入檔案,巢狀層次依賴於具體實現。

 

如果顯式路徑名為檔案識別符號的一部分,則僅在哪些子目錄中搜索被嵌入檔案。否則,如果檔名用雙引號括起來,則首先檢索當前工作目錄。如果未發現檔案,則在命令列中說明的所有目錄中搜索。如果仍未發現檔案,則搜尋實現時定義的標準目錄。

 

如果沒有顯式路徑名且檔名被尖括號括起來,則首先在編譯命令列中的目錄內檢索

 

如果檔案沒找到,則檢索標準目錄,不檢索當前工作目錄。

 

條件編譯命令

有幾個命令可對程式原始碼的各部分有選擇地進行編譯,該過程稱為條件編譯。商業軟體公司廣泛應用條件編譯來提供和維護某一程式的許多顧客版本。

 

#if、#else,#elif及#endif

 

#if的一般含義是如果#if後面的常量表達式為true,則編譯它與#endif之間的程式碼,否則跳過這些程式碼。命令#endif標識一個#if塊的結束。

 

#if constant-expression

statement sequence

#endif

 

跟在#if後面的表示式在編譯時求值,因此它必須僅含常量及已定義過的識別符號,不可使用變數。表示式不許含有操作符sizeof(sizeof也是編譯時求值)。

 

#else命令的功能有點象C語言中的else;#else建立另一選擇(在#if失敗的情況下)。注意,# else屬於# if塊。

 

#elif命令意義與ELSE IF 相同,它形成一個if else-if階梯狀語句,可進行多種編譯選擇。#elif 後跟一個常量表達式。如果表示式為true,則編譯其後的程式碼塊,不對其它#elif表示式進行測試。否則,順序測試下一塊。

 

#if expression

statement sequence

#elif expression1

statement sequence

#endif

 

在巢狀的條件編譯中#endif、#else或#elif與最近#if或#elif匹配。

 

# ifdef 和# ifndef

 

條件編譯的另一種方法是用#ifdef與#ifndef命令,它們分別表示"如果有定義"及"如果無定義"。# ifdef的一般形式是:

 

# ifdef macroname

statement sequence

#endif

 

#ifdef與#ifndef可以用於#if、#else,#elif語句中,但必須與一個#endif。

 

命令#undef 取消其後那個前面已定義過有巨集名定義。一般形式為:

#undef macroname

 

命令# line改變__LINE__與__FILE__的內容,它們是在編譯程式中預先定義的識別符號。命令的基本形式如下:

 

# line number["filename"]

 

其中的數字為任何正整數,可選的檔名為任意有效檔案識別符號。行號為源程式中當前行號,檔名為原始檔的名字。命令# line主要用於除錯及其它特殊應用。注意:在#line後面的數字標識從下一行開始的數字標識。

 

預定義的巨集名

 

ANSI標準說明了C中的五個預定義的巨集名。它們是:

 

__LINE__

__FILE__

__DATE__

__TIME__

__STDC__

 

如果編譯不是標準的,則可能僅支援以上巨集名中的幾個,或根本不支援。記住編譯程式也許還提供其它預定義的巨集名。

 

__LINE__及__FILE__巨集指令在有關# line的部分中已討論,這裡討論其餘的巨集名。

__DATE__巨集指令含有形式為月/日/年的串,表示原始檔被翻譯到程式碼時的日期。

原始碼翻譯到目的碼的時間作為串包含在__TIME__中。串形式為時:分:秒。

如果實現是標準的,則巨集__STDC__含有十進位制常量1。如果它含有任何其它數,則實現是非標準的。編譯C++程式時,編譯器自動定義了一個預處理名字__cplusplus,而編譯標準C時,自動定義名字__STDC__。

 

注意:巨集名的書寫由識別符號與兩邊各二條下劃線構成。

 

C、C++巨集體中出現的#,#@,##

 

巨集體中,#的功能是將其後面的巨集引數進行字串化操作(Stringfication),簡單說就是在對它所引用的巨集變數通過替換後在其左右各加上一個雙引號。

 

##被稱為連線符(concatenator),用來將兩個Token連線為一個Token。注意這裡連線的物件是Token就行,而不一定是巨集的變數。比如你要做一個選單項命令名和函式指標組成的結構體