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
%.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__
用於得到最後一次編譯的日期和時間(字串形式):
//-------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版本:
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__
用於得到編譯器的版本
#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
}
編譯器巨集使用總結 |
|
|