C语言再学习 -- C 预处理器
gcc/cc xxx.c ?可以編譯鏈接C源程序生成一個可執行文件 a.out
整個過程中可以劃分為以下的4步流程:
(1)預處理/預編譯: 主要用于包含頭文件的擴展,以及執行宏替換等 //加上 -E
(2)編譯:主要用于將高級語言程序翻譯成匯編語言,得到匯編語言 ? ?//加上 -S
(3)匯編:主要用于將匯編語言翻譯成機器指令,得到目標文件 ? ? ? ? ? //加上 -c
(4)鏈接:主要用于將目標文件和標準庫鏈接,得到可執行文件 ? ? ? ? ? //加上 -o
-E ?實現預處理的執行,默認將處理結果輸出到控制臺,可以通過-o選項指定輸出到xxx.i文件中,預處理文件中包含了很多頭文件,類型的別名,以及各種函數的聲明等等
-S ?實現編譯的處理,得到一個.s為后綴的匯編文件
-c ?實現匯編的處理,得到一個.o為后綴的目標文件
gcc/cc xxx.o 實現鏈接的處理,默認生成可執行文件a.out,可以通過選項-o來指定輸出文件名
參看:C語言再學習 -- GCC編譯過程
根據上面的描述我們可以知道預處理的所在時期,編譯程序之前,先由預處理器檢查程序,根據程序中使用的預處理指令,預處理器用符號縮略語所代表的內容替換程序中的縮略語。下面詳細介紹各個預處理指令:
一、#define的用法
#define 是一個預處理指令,這個預處理執行可以定義宏。與所有預處理指令一樣,預處理指令#define用#符號作為行的開頭。預處理指令從#開始,到其后第一個換行符為止。也就是說,指令的長度限于一行代碼。如果想把指令擴展到幾個物理行,可使用反斜線后緊跟換行符的方法實現,該出的換行符代表按下回車鍵在源代碼文件中新起一行所產生的字符,而不是符號 \n 代表的字符。在預處理開始錢,系統會刪除反斜線和換行符的組合,從而達到把指令擴展到幾個物理行的效果。可以使用標準C注釋方法在#define行中進行注釋。
//使用反斜線+回車 #define OW "hello\ world!" /*注意第二行要左對齊*/
每一個#define行由三部分組成:
第一部分,指令#deine自身。
第二部分,所選擇的縮略語,這些縮略語稱為宏(分為對象宏和函數宏)。宏的名字中不允許有空格,而且必須遵循C變量命名規則:只能使用字母、數字和下劃線(_),第一個字符不能為數字。習慣上宏名用大寫字母表示,以便于與變量區別。但也允許用小寫字母。
第三部分,(#define行的其余部分)稱為替換列表或主體。
注意,結尾沒有分號
下面來看一個例子:
#include <stdio.h>#define OW 2 * 2 #define OW 2 * 2 //#undef OW 需要先取消宏定義 #define OW 2*2int main (void) {printf ("%d\n", OW);return 0; } 輸出結果: define.c:5:0: 警告: “OW”重定義 [默認啟用] define.c:4:0: 附注: 這是先前定義的位置
#define OW 2 * 2
#define OW 2 ?* ?2
兩者都有三個相同的語言符號,而且額外的空格不是主體的一部分。下面的定義則被認為是不同的:
#define OW 2*2
上式只有一個(而非三個)語言符號,因此與前面兩個定義不同。可以使用#undef指令重新定義宏。
宏所代表的數字可以在編譯命令中指定(使用-D選項)
/*宏演示*/ #include <stdio.h> int main() {int num=0;int arr[SIZE]={}; //使用gcc -D可以宏定義這個數字for(num = 0;num <= SIZE - 1;num++){arr[num]=num;printf("%d ",arr[num]);}printf("\n");return 0; } gcc -DSIZE=4 define.c 輸出結果: 0 1 2 3
函數宏:
通過使用參數,可以創建外形和作用都與函數相似的類函數宏。宏的參數也用圓括號括起來。類函數宏的定義中,用圓括號括起來一個或多個參數,隨后這些參數出現在替換部分。
#include <stdio.h> #define SQUARE(X) X*X #define PR(X) printf ("The result is %d\n", X)int main (void) {int x = 4;int z;printf ("x = %d\n", x);z = SQUARE(x);printf ("Evaluating SQUARE(x): ");PR(z);z = SQUARE(2);printf ("Evaluating SQUARE(2): ");PR(z);printf ("Evaluating 100/SQUARE(2): ");PR(100/SQUARE(2));z = SQUARE(x+2);printf ("Evaluating SQUARE(x+2): ");PR(z);printf ("x is %d\n", x);z = SQUARE(++x);printf ("Eavluating SQUARE(++x): ");PR(SQUARE (++x));printf ("After incrementing, x is %x\n", x);return 0; } 輸出結果: x = 4 Evaluating SQUARE(x): The result is 16 Evaluating SQUARE(2): The result is 4 Evaluating 100/SQUARE(2): The result is 100 Evaluating SQUARE(x+2): The result is 14 x is 4 Eavluating SQUARE(++x): The result is 36 After incrementing, x is 6
SQUARE(x+2) 輸出結果是14,而不是想要的6*6 = 36。這是因為預處理器不進行計算,而只進行字符串替換。在出現x的地方,預處理都用字符串 x+2進行替換。x*x 變為 x+2*x+2 根據運算符優先級,則結果為 14
100/SQUARE(2)輸出結果是 100,而不是想要的 25。因為,根據優先級規則,表達式是從左到右求值的。
100/2*2 = 100
要處理前面兩個示例中的情況,需要如下定義:
#define SQUARE(x) ((x) * (x))
從中得到的經驗是使用必須的足夠多的圓括號來保證以正確的順序進行運行和結合。
SQUARE(++x) 根據編譯器的不同會出現兩種不同的結果。解決這個問題的最簡單的方法是避免在宏的參數中使用++x。一般來說,在宏中不要使用增量或減量運算符。
參看:C 語言再學習 -- 運算符與表達式
利用宏參數創建字符串:#運算符
在類函數宏的替換部分中,#符號用作一個預處理運算符,它可以把語言符號轉化為字符串。
例如:如果x是一個宏參量,那么#x可以把參數名轉化為相應的字符串。該過程稱為字符串化。
#include <stdio.h> #define PSQR(x) printf ("The square of " #x" is %d\n", ((x)*(x)))int main (void) {int y = 2;PSQR (y);PSQR (2 + 4);return 0; } 輸出結果: The square of y is 4 The square of 2 + 4 is 36 #include <stdio.h> #include <string.h> #define VEG(n) #n int main() {char str[20];strcpy(str,VEG(num));//num printf("%s\n",str);//拷貝return 0; } 輸出結果: num
預處理器的粘合劑:##運算符
和#運算符一樣,##運算符可以用于類函數宏的替換部分。另外,##還可用于類對象宏的替換部分。這個運算符把兩個語言符號組合成單個語言符號。
#include <stdio.h> #define XNAME(n) x##n #define PRINT_XN(n) printf ("x"#n" = %d\n", x##n)int main (void) {int XNAME (1) = 14; //變為 int x1 = 14;int XNAME (2) = 20; //變為 int x2 = 20;PRINT_XN (1); //變為 printf ("x1 = %d\n", x1);PRINT_XN (2); //變為 printf ("x2 = %d\n", x2);return 0; } 輸出結果: x1 = 14 x2 = 20
宏用于簡單函數:
#include <stdio.h> #define MAX(x,y) ((x)>(y) ? (x) : (y)) /*比較大小*/ #define ABS(x) ((x) < 0 ? -(x) : (x)) /*絕對值*/ #define ISSIGN(x) ((x) == '+' || (x) == '-' ? 1 : 0) /*正負號*/ int main() {printf ("較大的為: %d\n", MAX(5,3));printf ("絕對值為: %d\n", ABS (-2));printf ("正負號為: %d\n", ISSIGN ('+'));return 0; } 輸出結果: 較大的為: 5 絕對值為: 2 正負號為: 1
下面是需要注意的幾點:
1、宏的名字中不能有空格,但是在替代字符串中可以使用空格。ANSI C 允許在參數列表中使用空格。
2、用圓括號括住每個參數,并括住宏的整體定義。
3、用大寫字母表示宏函數名,便于與變量區分。
4、有些編譯器限制宏只能定義一行。即使你的編譯器沒有這個限制,也應遵守這個限制。
5、宏的一個優點是它不檢查其中的變量類型,這是因為宏處理字符型字符串,而不是實際值。
面試:用預處理指令#define 聲明一個常數,用以表明1年中有多少秒(忽略閏年問題)?
#define SEC (60*60*24*365)UL
考察內容:
1、懂得預處理器將為你計算常量表達式的值,因此,可直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,這樣更清晰而沒有代價。
2、意識到這個表達式將使一個16 位機的整形數溢出,因此要用到長整形符號 L ,告訴編譯器這個常數是長整形數。
3、如果你在你的表達式中用到UL(表示無符號長整型),那么你有了一個好的起點。
面試:寫一個“標準”宏MIN ,這個宏輸入兩個參數并返回較小的一個
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
考察內容:
1、三目表達式的使用
2、使用必須的足夠多的圓括號來保證以正確的順序進行運行和結合
3、進一步討論,在宏中不要使用增量或減量運算符
參看:宏名必須用大寫字母嗎?
研究:C語言中用宏定義(define)表示數據類型和用typedef定義數據類型有什么區別?
宏定義只是簡單的字符串代換,是在預處理完成的,而typedef是在編譯時處理的,它不是作簡單的代換,而是對類型說明符重新命名。被命名的標識符具有類型定義說明的功能。
請看下面的例子:
#define P1 int *
typedef (int *) P2
從形式上看這兩者相似,但在實際使用中卻不相同。
下面用P1、P2說明變量時就可以看出它們的區別:
P1 a, b; ?在宏代換后變成: int *a, b; ?表示 a 是指向整型的指針變量,而 b 是整型變量。
P2 a, b; ?表示a,b都是指向整型的指針變量。因為PIN2是一個類型說明符。
由這個例子可見,宏定義雖然也可表示數據類型, 但畢竟是作字符代換。在使用時要分外小心,以避出錯。
總結,typedef和#define的不同之處:
1、與#define不同,typedef 給出的符號名稱僅限于對類型,而不是對值。
2、typedef 的解釋由編譯器,而不是是處理器執行。
3、雖然它的范圍有限,但在其受限范圍內,typedef 比 #define 更靈活。
用于定義字符串,尤其是路徑
A),#define ENG_PATH_1 E:\English\listen_to_this\listen_to_this_3 ? ? ? ? ? ? ??
B),#define ENG_PATH_2 “ E:\English\listen_to_this\listen_to_this_3”
A 為 定義路徑, B ?為定義字符串
C), #define ENG_PATH_3 E:\English\listen_to_this\listen\
_to_this_3
還沒發現問題?這里用了 4 個反斜杠,到底哪個是接續符?回去看看接續符反斜杠。反斜杠作為接續符時,
在本行其后面不能再有任何字符,空格都不行。所以,只有最后一那給 ENG_PATH_1 加上雙引號不就成了:“ENG_PATH_1”。但是請注意:有的系統里規定路徑的要用雙反斜杠“ \\” ,比如:
#define ENG_PATH_4 E:\\English\\listen_to_this\\listen_to_this_3
二、#undef 指令
取消定義一個給定的 #define。
例如有如下宏定義:
#define LIMIT 40
則指令
#undef LIMIT
會取消該定義。
現在就可以重新定義LIMIT,以使它有一個新的值。即使開始沒有定義LIMIT,取消LIMIT的定義也是合法的。如果想使用一個特定名字,但又不能確定前面是否已經使用了該名字,為安全起見,就可以取消該名字的定義。
注意:#define 宏的作用域從文件中的定義點開始,直到用 #undef 指令取消宏為止,或直到文件尾為止(由二者中最先滿足的那個結束宏的作用域)。還應注意,如果用頭文件引入宏,那么,#define 在文件中的位置依賴 #define 指令的位置。
#include <stdio.h> #define X 3 #define Y X*3 #undef X #define X 2int main (void) {printf ("Y = %d\n", Y);printf ("X = %d\n", X);return 0; } 輸出結果: Y = 6 X = 2 #include <stdio.h> #define X 3 #define Y X*3 #define X 2 //不可重復定義int main (void) { int z = Y;printf ("Y = %d\n", z);printf ("X = %d\n", X);return 0; } 輸出結果: test.c:4:0: 警告: “X”重定義 [默認啟用] test.c:2:0: 附注: 這是先前定義的位置
三、文件包含:#include
預處理器發現#include指令后,就會尋找后跟的文件名并把這個文件的內容包含但當前文件中。被包含文件中的文件將替換源代碼文件中的#include指令,就像你把被包含文件中的全部內容鍵入到源文件中的這個特定位置一樣。
#include 指令有兩種使用形式:
1) #include <filename.h> ? ?文件名放在尖括號中
在UNIX系統中,尖括號告訴預處理器在一個或多個標準系統目錄中尋找文件。?
如: #include <stdio.h>
查看:
ls /usr/include
ls kernel/include?
2) #include "filename.h" ? ?文件名放在雙引號中
在UNIX系統中,雙引號告訴預處理器現在當前目錄(或文件名中指定的其他目錄)中尋找文件,然后在標準位置尋找文件。
如: #include "hot.h" ? ? #include "/usr/buffer/p.h"
習慣上使用后綴 .h 表示頭文件,這類文件包含置于程序頭部的信息。頭文件經常包含預處理語句。有些頭文件由系統提供。但也可以自由創建自己的頭文件。
擴展:C語言再學習 -- 常用頭文件和函數(轉)
Lniux的文件系統中有一個大分組,它包含了文件系統中所有文件,這個大的分組用一個專門的目錄表示,這個目錄叫做根目錄,根目錄可以使用“/”表示。
路徑可以用來表示文件或者文件夾所在的位置,路徑是從一個文件夾開始走到另一個文件夾或者文件位置中間的這條路。把這條路經過的所有文件夾名稱按順序書寫出來的結果就可以表示這條路。
路徑分為絕對路徑和相對路徑
絕對路徑:起點必須是根目錄,如 /abc/def ?所有絕對路徑一定是以“/”作為開頭的
相對路徑:可以把任何一個目錄作為起點,如../../abc/def ?相對路徑編寫時不應該包含起點位置
相對目錄中“..”表示上層目錄
相對路徑中用“.”表示當前
終端窗口里的當前目錄是所有相對路徑的起點,當前目錄的位置是可以修改的。
pwd 命令可以用來查看當前目錄的位置
cd ?命令可以用來修改當前目錄位置 ??
ls ?命令可以用來查看一個目錄的內容
四、條件編譯
參看:條件編譯#ifdef的妙用詳解_透徹
#if:表示如果...
#ifdef: 表示如果定義...
#ifndef:表示如果沒有定義...
#else: 表示否則...與#ifdef/#ifndef搭配使用 ?//筆試題 ?注意,沒有#elseif
#elif: 表示否則如果...與#if/#ifdef/#ifndef搭配使用
#endif: 表示結束判斷,與#if/#ifdef/#ifndef搭配使用
注意:#if 和 if 區別
#if=>主要用于編譯期間的檢查和判斷
if ? =>主要用于程序運行期間的檢查和判斷Z
最常見的形式:
#ifdef 標識符 程序段1 #else 程序段2 #endif
作用:當標識符已經被定義過(一般用#define命令定義),則對程序段1進行編譯,否則編譯程序段2。其中#else部分也可以沒有,即:
#ifdef 標識符 程序段1 #endif 這里的“程序段”可以是語句組,也可以是命令行。這種條件編譯可以提高C源程序的通用性。如果一個C源程序在不同計算機系統上運行,而不同的計算機又有一定的差異。例如,我們有一個數據類型,在Windows平臺中,應該使用long類型表示,而在其他平臺應該使用float表示,這樣往往需要對源程序做必要的修改,這就降低了程序的通用性。可以用以下的條件編譯: #ifdef WINDOWS #define MYTYPE long #else #define MYTYPE float #endif 如果在Windows上編譯程序,則可以在程序的開始加上 #define WINDOWS 這樣則編譯下面的命令行: #define MYTYPE long 如果在這組條件編譯命令之前曾出現以下命令行: #define WINDOWS 0 則預編譯后程序中的MYTYPE都用float代替。這樣,源程序可以不必任何修改就可以用于不同類型的計算機系統。當然以上介紹的只是一種簡單的情況,可以根據此思路設計出其他的條件編譯。例如,在調試程序時,常常希望輸出一些所需的信息,而在調試完成后不再輸出這些信息。可以在源程序中插入以下的條件編譯段:
#ifdef DEBUG print ("device_open(%p)\n", file); #endif 如果在它的前面有以下命令行: #define DEBUG 則在程序運行時輸出file指針的值,以便調試分析。調試完成后只需將這個define命令行刪除即可。有人可能覺得不用條件編譯也可以達到此目的,即在調試時加一批printf語句,調試后一一將prntf語句刪除。的確,這是可以的。但是,當調試時加的printf語句比較多時,修改的工作量是很大的。用條件編譯,則不必一一刪除printf語句。只需刪除前面的一條#define DEBUG 命令即可,這時所有的用DEBUG 作標識符的條件編譯段都使其中的printf語句不起作用,起到統一控制的作用,如同一個“開關”一樣。有時也采用下面的形式:
#ifndef 標識符 程序段1 #else 程序段2 #endif只是第一行與第一種形式不同:將“#ifdef”改為“#ifndef”。它的作用是,若標識符未被定義則編譯程序段1,否則編譯程序段2。這種形式與第一種形式的作用相反。
一般地,當某文件包含幾個頭文件,而且每個頭文件都可能定義了相同的宏,使用#ifndef可以防止該宏重復定義。
/*test.h*/ #ifndef SIZE #define SIZE 100 #endif
#ifndef 指令通常用于防止多次包含同一文件,也就是說,頭文件可采用類似下面幾行的設置:
//頭文件衛士 #ifndef THINGS_H_ #define THINGS_H_ #endif還有一種形式,就是#if 后面跟一個表達式,而不是一個簡單的標識符:
#if 表達式 程序段1 #else 程序段2 #endif 它的作用是:當指定的表達式為真(非零)時就編譯程序段1,否則編譯程序段2.可以事先給定一定條件,使程序在不同的條件下執行不同的功能。例如: #include <stdio.h> #define LETTER 1int main (void) {#if LETTERprintf ("111\n");#elseprintf ("222\n");#endifreturn 0; } 輸出結果: 111 這種形式也可以用作注釋用:#if 1 ?和 #if 0 #include <stdio.h> int main (void) {#if 0printf ("111\n");#endifprintf ("222\n");return 0; } 輸出結果: 222最后一種形式
#if 標識符 #elif 程序段1 #elfi 程序段2 。。。 #else 程序段n #endif #if...#elif(任意多次)...#else...#endif,以上結構可以從任意邏輯表達式選擇一組編譯,這種結構可以根據任意邏輯表達式進行選擇。
/*條件編譯演示*/ #include <stdio.h> #define SAN int main() { #if defined(YI) //布爾值printf("1\n"); #elif defined(ER) //布爾值printf("2\n"); #elif defined(SAN) printf("3\n"); #elseprintf("4\n"); #endifreturn 0; } 輸出結果: 3 這里,define是一個預處理運算符。如果 define 的參數已用#define定義過,那么define返回1,否則返回 0 。這種方法的優點在于它可以和#elif一起使用。
應用示例:
我們主要使用以下幾種方法,假設我們已在程序首部定義:
#define DEBUG
#define TEST
1、利用#ifdef / #endif 將程序功能模塊包括進去,以向某用戶提供該功能.
在程序首部定義#define HNLD: #ifdef HNLD include"n166_hn.c" #endif 如果不許向別的用戶提供該功能,則在編譯之前將首部的HNLD加下劃線即可。
2、在每一個子程序前加上標記,以便追蹤程序的運行。
#ifdef DEBUG printf(" Now is in hunan !"); #endif
#ifndef TEST i=dial(); //程序調試運行時繞過此語句 #else i=0; #endif 調試通過后,再屏蔽TEST的定義并重新編譯,即可發給用戶使用了。
有一個問題,如何確保使用的標識符在其他任何地方都沒有定義過?
通常編譯器提供商采用下述方法解決這個問題:用文件名做標識符,并在文件名中使用大寫字母、用下劃線代替文件名中的句點字符、用下劃線(可能使用兩條下劃線)做前綴和后綴。例如,檢查頭文件read.h,可以發現許多類似的語句:
#ifndef __READ_H__ //作為開頭的預處理指令則當它后面的宏名稱被定義過則編譯后一組否則編譯前一組 #define __READ_H__ //防止被重復定義 extern int num=0; #endif __READ_H__參看:C語言再學習 -- 標識符
擴展:extern "C"
通過 extern "C" 可以要求 C++ 編譯器按照 C方式處理函數接口,即不做換名,當然也就無法重載。
1) C 調 C++,在 C++ 的頭文件如下設置:
extern "C" int add (int x, int y); extern "C" { int add (int x, int y); int sub (int x, int y); } //示例 add.h #ifndef _ADD_H #define _ADD_H #ifdef __cplusplus extern "C" { #endif int add (int ,int ); #ifdef __cplusplus } #endif #endif
2)C++ 調 C,在C++ 的主函數如下設置:
extern "C" { #include "chead.h" } //示例 main.cpp #include <iostream> using namespace std; extern "C" { #include "05sub.h" } int main (void) {int x=456,y=123;cout << x << "+" << y << "="<< sub(x, y) << endl;return 0; }
五、預定義宏
__DATE__進行預處理的日期(“Mmm dd yyyy”形式的字符串文字)
__FILE__代表當前源代碼文件名的字符串文字
__BASE_FILE__獲取正在編譯的源文件名
__LINE__代表當前源代碼文件中的行號的整數常量
__TIME__源文件編譯時間,格式為“hh: mm: ss”
__STDC__設置為 1時,表示該實現遵循 C標準
__STDC_HOSTED__為本機環境設置為 1,否則設為 0
__STDC_VERSION__為C99時設置為199901L
__FUNCTION__或者 __func__ ?獲取所在的函數名(預定義標識符,而非預定義宏)
#include <stdio.h> int main (void) {printf ("The file is %s\n", __FILE__);printf ("The base_file is %s\n", __BASE_FILE__);printf ("The line is %d\n", __LINE__);printf ("The function is %s\n", __FUNCTION__);printf ("The func is %s\n", __func__);printf ("The date is %s\n", __DATE__);printf ("The time is %s\n", __TIME__);return 0; } 輸出結果: The file is part.c The base_file is part.c The line is 6 The function is main The func is main The date is Nov 22 2016 The time is 15:46:30
六、常用的新指令
#line?整數n =>表示修改代碼的行數/指定行號 ? 插入到程序中表示從行號n開始執行,修改下一行的行號為n
#error ?字符串 => 表示產生一個錯誤信息
#warning 字符串 => 表示產生一個警告信息
//#line 預處理指令的使用 #include <stdio.h> #line 200 int main(void) {printf("The line is %d\n",__LINE__);return 0; } 輸出結果: The line is 202 //#error和#warning的使用 #include <stdio.h>#define VERSION 4 #define VERSION 2 #define VERSION 3 #if(VERSION < 3)#error "版本過低" #elif(VERSION > 3)#warning "版本過高" #endifint main(void) {printf("程序正常運行\n");return 0; } 輸出結果: 警告: #warning "版本過高" //錯誤: #error "版本過低" //程序正常運行
七、#pragma
#pragma GCC dependency 文件名?
表示當前文件依賴于指定的文件,如果當前文件的最后一次,修改的時間早于依賴的文件,則產生警告信息
#pragma GCC poison 標示符
表示將后面的標示符設置成毒藥,一旦使用標示符,則產生錯誤或警告信息
//毒藥的設置 #include <stdio.h> //#define GOTO goto //將goto設置為毒藥 #pragma GCC poison gotoint main(void) {//GOTO ok;goto ok;printf("main函數開始\n");ok:printf("main函數結束\n");return 0; } 輸出結果: 錯誤: 試圖使用有毒的“goto”
#pragma pack (整數n)
表示按照整數n倍進行補齊和對齊
//設置結構體的對齊和補齊方式 #include <stdio.h>//設置結構體按照2的整數倍進行對齊補齊 #pragma pack(2) //8 //#pragma pack(1) //6 //#pragma pack(3) //error //char short int long int long longint main(void) {struct S{char c1;int i;char c2;};printf("sizeof(struct S) = %d\n",sizeof(struct S));//12return 0; } 輸出結果: sizeof(struct S) = 8
#pragma message
message 參數: message 參數是我最喜歡的一個參數,它能夠在編譯信息輸出窗,口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:
#pragma message(“消息文本”)
當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什么地方定義了_X86 這個宏可以用下面的方法.
#define _X86 #ifdef _X86 #pragma message ("_X86 macro activated!") #endif 輸出結果: 附注: #pragma message:_X86 macro activated! 當我們定義了_X86 這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示“_X86 macro activated!”。我們就不會因為不記得自己定義的一些特定的宏而抓耳撓腮了.
#pragma code_seg
另一個使用得比較多的 pragma 參數是 code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能夠設置程序中函數代碼存放的代碼段,當我們開發驅動程序的時候就會使用到它。
#pragma once
#pragma once (比較常用)
只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在Visual C++6.0 中就已經有了,但是考慮到兼容性并沒有太多的使用它。
#pragma hdrstop
#pragma hdrstop 表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。 BCB 可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除一些頭文件。有時單元之間有依賴關系,比如單元 A 依賴單元 B,所以單元 B 要先于單元 A 編譯。你可以用#pragma startup 指定編譯優先級,如果使用了#pragma package(smart_init) , BCB就會根據優先級的大小先后編譯。
#pragma resource
#pragma resource "*.dfm"表示把*.dfm 文件中的資源加入工程。 *.dfm 中包括窗體外觀的定義。
#pragma warning
#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)。
#pragma comment
#pragma comment(...)
該指令將一個注釋記錄放入一個對象文件或可執行文件中。常用的 lib 關鍵字,可以幫我們連入一個庫文件。 比如:
#pragma comment(lib, "user32.lib")
該指令用來將 user32.lib 庫文件加入到本工程中。linker:將一個鏈接選項放入目標文件中,你可以使用這個指令來代替由命令行傳入的或者在開發環境中設置的鏈接選項,你可以指定/include 選項來強制包含某個對象,例如:
#pragma comment(linker, "/include:__mySymbol")
總結
以上是生活随笔為你收集整理的C语言再学习 -- C 预处理器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前后端分离Java后端跨越问题解决
- 下一篇: BIO与NIO、AIO的区别