當前位置:首頁 » 編程軟體 » 預定義宏在哪裡編譯

預定義宏在哪裡編譯

發布時間: 2023-02-23 04:06:33

⑴ C語言,#define語句的用法。

1.簡單的宏定義

格式:#define 標識符 替換列表

說明:替換列表可以是數,字元串字面量,標點符號,運算符,標識符,關鍵字,字元常量。

注意:替換列表是可以為空的

典型錯誤:

#defineN=100
inta[N];/*這樣會成為inta[=100],這里會處理成為一種標識記號一樣*/


#defineN100;
inta[N];/*帶分號的定義會成為inta[100;],這是一種很常見的錯誤*/

#definepin(int*);
pina,b;
int*a,b;/*本意是a和b都是int型指針,但是實際上變成int*a,b;a是int型指針,而b是int型變數。這是應該使用typedef來代替define,這樣a和b就都是int型指針了。*/

典型的使用方法:

使用宏定義可以自己根據自己的習慣來定義甚至改變C語言的語法習慣,例如:

#defineBEGIN{
#defineEND}
intmain()BEGIN
printf("DEFINE---- ");
END

定義一個循環

#defineLOOPfor(;;)

重新定義數據類型

#defineITint

2.帶參數的宏

#define 標識符(x1,x2,x3...xn) 替換列表 (注意:x1,x2,x3..是宏的參數,標識符和其左括弧之間不能有空格)

使用方法:

#defineMAX(x,y)((x)>(y)?(x):(y))
i=MAX(j+k,m-n);

替換為:

i=MAX((j+k)>(m-n)?(j+k):(m-n));
#defineSI_EX(n)((n)%2==0)
if(SI_EX(i))i++;

替換為:

if(SI_EX((i)%2==0))

通過以上例子可以看出,標識符帶參數(X1,X2,X3.....)在替換時會被替換列表(Y1,Y2,Y3....)對應的替換,但是和順序無關。

3.宏的特殊單行定義

#defineA(x)T_##x
#defineB(x)#@x
#defineC(x)#x
x=1

替換為:

A(1)------> T_1 (T_##x,##符號是記號粘黏符號,將前後的字元粘黏起來。)

B(1)------> '1' ( #@x , #@ 符號會將宏的參數進行字元串字面量化,並且加『』號)

C(1)------> "1" ( #x ,#符號會將宏的參數進行字元串字面量化,並且加""號)

4.define的多行定義

define可以替代多行的代碼,在每一個換行的時候加上一個""

#defineMAX(X,Y)do{
語句1;
語句2;
/*注釋的寫法*/
}while(0)/*(notrailing;)*/

5.在大規模的開發過程中,特別是跨平台和系統的軟體里,define最重要的功能是條件編譯

#ifdefWINDOWS
......
......
#endif
#ifdefLINUX
......
......
#endif

可以在編譯的時候通過#define設置編譯環境

6.取消宏

#undef標識符

7.條件編譯

#ifdefXXX…(#else)…#endif


8.預定義宏

在C語言中收錄了一些有用的宏,這些宏是提供當前編譯信息的。

__LINE__ 被編譯文件的行數

(整型)

__FILE__ 被編譯文件的名字 (字元型)

__DATE__ 編譯日期 (字元型)

__TIME__ 編譯時間 (字元型)

__STDC__ 如果編譯器接受標准C,那麼值為1. (整型)

通常在編譯的時候可以用這些預定義的宏來進行排錯,例如:

一般情況下,當出現除零的情況時編譯器是沒有提示的,可以自定義一個解決辦法。

#defineCHECKZERO(divisor)
if(divisor==0)
printf("***ATTEMPTTODIVIDEBYZEROINLINE%doffile%s*** ",__LINE__,__FILE__);

引用方法

CHECKZERO(j);
k=i/j;

⑵ 如何在C語言中用宏來判斷當前編譯器

熱心網友
一.
#define是C語言中提供的宏定義命令,其主要目的是為程序員在編程時提供一定的方便,並能在一定程度上提高程序的運行效率,但學生在學習時往往不能理解該命令的本質,總是在此處產生一些困惑,在編程時誤用該命令,使得程序的運行與預期的目的不一致,或者在讀別人寫的程序時,把運行結果理解錯誤,這對 C語言的學習很不利。
1#define命令剖析
1.1 #define的概念
#define命令是C語言中的一個宏定義命令,它用來將一個標識符定義為一個字元串,該標識符被稱為宏名,被定義的字元串稱為替換文本。
該命令有兩種格式:一種是簡單的宏定義,另一種是帶參數的宏定義。
(1) 簡單的宏定義:
#define <宏名><字元串>
例: #define PI 3.1415926
(2) 帶參數的宏定義
#define <宏名> (<參數表>) <宏體>
例: #define A(x) x
一個標識符被宏定義後,該標識符便是一個宏名。這時,在程序中出現的是宏名,在該程序被編譯前,先將宏名用被定義的字元串替換,這稱為宏替換,替換後才進行編譯,宏替換是簡單的替換。
1.2 宏替換發生的時機
為了能夠真正理解#define的作用,讓我們來了解一下對C語言源程序的處理過程。當我們在一個集成的開發環境如Turbo C中將編寫好的源程序進行編譯時,實際經過了預處理、編譯、匯編和連接幾個過程,見圖1。

⑶ c++當中的預編譯宏的問題

#ifndef HY //一般在頭文件都都使用這種格式以防止頭文件在同一文件中被包含兩次。語名#ifndef hyong…..#endi表示僅當以前沒有使用預處理器編譯指令#define定義的名稱hyong時才處理#ifndef….#endif之間的語句。

#define HY //在這里用define定義一個名字HY,以便在下次訪問到該頭文件時,使該名字已經被定義,從而讓程序跳過#ifndef….#endif間的語句。

...

#endif

明白了??
如果還不明白,就下載本人的文章《C++名稱空間與作用域專題》和《C++宏,預處理器,RTTI,typeid與強制類型轉換》去了解了解吧。

⑷ C++中怎樣寫宏定義

C/C++中宏使用總結
.C/C++中宏總結C程序的源代碼中可包括各種編譯指令,這些指令稱為預處理命令。雖然它們實際上不是C語言的一部分,但卻擴展了C程
序設計的環境。本節將介紹如何應用預處理程序和注釋簡化程序開發過程,並提高程序的可讀性。

ANSI標準定義的C語言預處理程序包括下列命令:

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

1、#define

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

#define identifier string

注意:

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

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

? 宏替換僅僅是以文本串代替宏標識符,前提是宏標識符必須獨立的識別出來,否則不進行替換。例如: #define XYZ
this is a test,使用宏printf("XYZ");//該段不列印"this is a test"而列印"XYZ"。因為預編譯器識
別出的是"XYZ"

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

2、#error

處理器命令#error強迫編譯程序停止編譯,主要用於程序調試。

3、#i nclude

命令#i nclude使編譯程序將另一源文件嵌入帶有#i nclude的源文件,被讀入的源文件必須用雙引號或尖括弧括起來。例如:

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

這兩行代碼均使用C編譯程序讀入並編譯用於處理磁碟文件庫的子程序。

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

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

如果沒有顯式路徑名且文件名被尖括弧括起來,則首先在編譯命令行中的目錄內檢索。

如果文件沒找到,則檢索標准目錄,不檢索當前工作目錄。

4、條件編譯命令

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

#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。

5、#undef

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

#undef macroname

6、#line

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

# line number["filename"]

其中的數字為任何正整數,可選的文件名為任意有效文件標識符。行號為源程序中當前行號,文件名為源文件的名字。命令# line主要用於調試及其它特殊
應用。

注意:在#line後面的數字標識從下一行開始的數字標識。

7、預定義的宏名

ANSI標准說明了C中的五個預定義的宏名。它們是:

__LINE__

__FILE__

__DATE__

__TIME__

__STDC__

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

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

__DATE__宏指令含有形式為月/日/年的串,表示源文件被翻譯到代碼時的日期。

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

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

注意:宏名的書寫由標識符與兩邊各二條下劃線構成。

(部分內容出自:http://www.bc-cn.net/Article/kfyy/cyy/jc/200511/919.html)

8、C、C++宏體中出現的#,#@,##

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

而##被稱為連接符(concatenator),用來將兩個Token連接為一個Token。注意這里連接的對象是Token就行,而不一定是宏的變
量。比如你要做一個菜單項命令名和函數指針組成的結構體的數組,並且希望在函數名和菜單項命令名之間有直觀的、名字上的關系。那就可以使用:宏參數##
固定部分。當然還可以n個##符號連接 n+1個Token,這個特性也是#符號所不具備的。

#@的功能是將其後面的宏參數進行字元化。

9、C宏中的變參...

...在C宏中稱為Variadic Macro,也就是變參宏。比如:

#define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)

或者#define myprintf(templt,args...) fprintf(stderr,templt,args)

第一個宏中由於沒有對變參起名,我們用默認的宏__VA_ARGS__來替代它。第二個宏中,我們顯式地命名變參為args,那麼我們在宏定義中就可以
用args來代指變參了。同C語言的stdcall一樣,變參必須作為參數表的最後有一項出現。當上面的宏中我們只能提供第一個參數templt時,C
標准要求我們必須寫成: myprintf(templt,);的形式。這時的替換過程為:myprintf("Error!\n",);替換為:
fprintf(stderr,"Error!\n",).

這是一個語法錯誤,不能正常編譯。這個問題一般有兩個解決方法。首先,GNU CPP提供的解決方法允許上面的宏調用寫成:
myprintf(templt);而它將會被通過替換變成: fprintf(stderr,"Error!\n",);

很明顯,這里仍然會產生編譯錯誤(非本例的某些情況下不會產生編譯錯誤)。除了這種方式外,c99和GNU CPP都支持下面的宏定義方式:

#define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)

這時,##這個連接符號充當的作用就是當__VAR_ARGS__為空的時候,消除前面的那個逗號。那麼此時的翻譯過程如下:
myprintf(templt);被轉化為: fprintf(stderr,templt);

這樣如果templt合法,將不會產生編譯錯誤。

10、#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號警告信息

#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)用pragma導出dll中的函數

傳統的到出 DLL 函數的方法是使用模塊定義文件 (.def),Visual C++ 提供了更簡潔方便的方法,那就
是"__declspec()"關鍵字後面跟"dllexport",告訴連接去要導出這個函數,例如:

__declspec(dllexport) int __stdcall MyExportFunction(int iTest);

把"__declspec(dllexport)"放在函數聲明的最前面,連接生成的 DLL 就會導出函
數"_MyExportFunction@4"。

上面的導出函數的名稱也許不是我的希望的,我們希望導出的是原版的"MyExportFunction"。還好,VC 提供了一個預處理指示
符"#pragma"來指定連接選項 (不僅僅是這一個功能,還有很多指示功能) ,如下:

#pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")

這下就天如人願了:)。如果你想指定導出的順序,或者只將函數導出為序號,沒有 Entryname,這個預處理指示符 (確切地說是連接器) 都能夠
實現,看看 MSDN 的語法說明:

/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]

@ordinal 指定順序;NONAME 指定只將函數導出為序號;DATA 關鍵字指定導出項為數據項。

⑨每個編譯程序可以用#pragma指令激活或終止該編譯程序支持的一些編譯功能。例如,對循環優化功能:

#pragma loop_opt(on) // 激活

#pragma loop_opt(off) // 終止

有時,程序中會有些函數會使編譯器發出你熟知而想忽略的警告,如"Parameter xxx is never used in function
xxx",可以這樣:

#pragma warn -100 // Turn off the warning message for warning #100

int insert_record(REC *r)

{ /* function body */ }

#pragma warn +100 // Turn the warning message for warning #100 back
on

函數會產生一條有唯一特徵碼100的警告信息,如此可暫時終止該警告。

每個編譯器對#pragma的實現不同,在一個編譯器中有效在別的編譯器中幾乎無效。可從編譯器的文檔中查看。
⑩#pragm pack()的使用

#pragma pack規定的對齊長度,實際使用的規則是:

? 結構,聯合,或者類的數據成員,第一個放在偏移為0的地方,以後每個數據成員的對齊,按照#pragma pack指定的數值和這
個數據成員自身長度中,比較小的那個進行。

? 也就是說,當#pragma pack的值等於或超過所有數據成員長度的時候,這個值的大小將不產生任何效果。

? 而結構整體的對齊,則按照結構體中最大的數據成員 和 #pragma pack指定值之間,較小的那個進行。

注意:文件使用#pragma pack(n) 改變了預設設置而不恢復,通常可以使用#pragma pack(push, n)和#pragma
pack(pop)進行設置與恢復。

註:關於宏函數的內容在另外的專題。關於宏使用的誤區在描述宏的時候已經在文中提到了,最後再給出一個例子,描述的Side Effect是指宏在展開
的時候對其參數可能進行多次Evaluation(也就是取值)對程序造成的錯誤影響。

假設在一個系統中,有一個32b的寄存器(REG)保存狀態,其中高16b表示一種含義,低16b表示另一種含義(這在程序中經常出現)。現在要把高低
16b分開,不考慮實際中的特殊要求,將代碼寫成:

#define High16bit(REG) (REG>>16)

#define Low16bit(REG) ((REG<<16)>>16)

對於這種寫法完成的功能在大多數情況是足夠了,這里不討論。主要談論這種寫法的負面影響,如果在程序中分別在不同的語句中使用High16bit和
Low16bit,那麼就可能那就是Side effect,特別寄存器REG是狀態寄存器,他的狀態可能隨時變化,那麼引起的問題就是高低16b根本
取的不是同一個時刻狀態寄存器。這種錯誤在程序中找出就比較難了。在這里我把條件弱化了,試想在一個宏體中,如果對參數多次取值也是可能引起問題,那就 更難了。

⑸ 編譯器預定義的宏(可以用來區分使用的是哪種編譯器) 詳細�0�3

1、_MSC_VER 是微軟C/C++編譯器——cl.exe 編譯代碼時預定義的一個宏。需 要針對cl 編寫代碼時, 可以使用該宏進行條件編譯。 2、_MSC_VER 的值表示cl 的版本。需要針對cl 特定版本編寫代碼時, 也可以使用 該宏進行條件編譯。 3、_MSC_VER 的類型是"int",具體版本號定義如下: MS VC++ 9.0 _MSC_VER = 1500 MS VC++ 8.0 _MSC_VER = 1400 MS VC++ 7.1 _MSC_VER = 1310 MS VC++ 7.0 _MSC_VER = 1300 MS VC++ 6.0 _MSC_VER = 1200 MS VC++ 5.0 _MSC_VER = 1100 其中MS VC++ 9.0 就是Visual C++ 2008,MS VC++ 8.0 就是Visual C++2005。 二、介紹預定義宏「__GNUC__」 1、__GNUC__ 是gcc 編譯器編譯代碼時預定義的一個宏。需要針對gcc 編寫代碼時, 可以使用該宏進行條件編譯。 2、__GNUC__ 的值表示gcc 的版本。需要針對gcc 特定版本編寫代碼時,也可以使 用該宏進行條件編譯。 3、__GNUC__ 的類型是「int」 三、預定義宏"__MINGW32__" 1、MinGW編譯器 四、symbian sdk 預定義宏: symbian 平台,定義"__SYMBIAN32_" 3rd MR 版及之前的那個3rd 版本,定義"__SERIES60_30__" 3rd FP1 版,定義"__SERIES60_31__" 3rd FP2 版,定義"__SERIES60_32__" 另外,還有一個"__SERIES60_3x__"。若不需區分具體是哪一個3rd 版,則用之。

熱點內容
java返回this 發布:2025-10-20 08:28:16 瀏覽:746
製作腳本網站 發布:2025-10-20 08:17:34 瀏覽:1010
python中的init方法 發布:2025-10-20 08:17:33 瀏覽:716
圖案密碼什麼意思 發布:2025-10-20 08:16:56 瀏覽:878
怎麼清理微信視頻緩存 發布:2025-10-20 08:12:37 瀏覽:774
c語言編譯器怎麼看執行過程 發布:2025-10-20 08:00:32 瀏覽:1124
郵箱如何填寫發信伺服器 發布:2025-10-20 07:45:27 瀏覽:349
shell腳本入門案例 發布:2025-10-20 07:44:45 瀏覽:227
怎麼上傳照片瀏覽上傳 發布:2025-10-20 07:44:03 瀏覽:911
python股票數據獲取 發布:2025-10-20 07:39:44 瀏覽:873