本文轉自博文C/C++預處理指令#define,#ifdef,#ifndef,#endif…。這篇博文寫得特別好,特轉載。
本文主要記錄了C/C++預處理指令,常見的預處理指令如下:
- #空指令,無任何效果
- #include包含一個源代碼文件
- #define定義宏
- #undef取消已定義的宏
- #if如果給定條件為真,則編譯下面代碼
- #ifdef如果宏已經定義,則編譯下面代碼
- #ifndef如果宏沒有定義,則編譯下面代碼
- #elif如果前面的#if給定條件不為真,當前條件為真,則編譯下面代碼
- #endif結束一個#if……#else條件編譯塊
- #error停止編譯並顯示錯誤信息
本來只是想了解一下#ifdef,#ifndef,#endif的,沒想到查出來這么多的預處理指令,上面的多數都是常見的,但是平時沒有怎么注意預處理這方面的內容,所以這里梳理一下知識吧。同時有什么不妥的地方,或者遺漏了什么內容,還請留言指出。
什么是預處理指令?
預處理指令是以#號開頭的代碼行。#號必須是該行除了任何空白字符外的第一個字符。#后是指令關鍵字,在關鍵字和#號之間允許存在任意個數的空白字符。整行語句構成了一條預處理指令,該指令將在編譯器進行編譯之前對源代碼做某些轉換。
以前沒有在意的學者注意了,預處理指令是在編譯器進行編譯之前進行的操作.預處理過程掃描源代碼,對其進行初步的轉換,產生新的源代碼提供給編譯器。可見預處理過程先於編譯器對源代碼進行處理。在很多編程語言中,並沒有任何內在的機制來完成如下一些功能:在編譯時包含其他源文件、定義宏、根據條件決定編譯時是否包含某些代碼(防止重復包含某些文件)。要完成這些工作,就需要使用預處理程序。盡管在目前絕大多數編譯器都包含了預處理程序,但通常認為它們是獨立於編譯器的。預處理過程讀入源代碼,檢查包含預處理指令的語句和宏定義,並對源代碼進行響應的轉換。預處理過程還會刪除程序中的注釋和多余的空白字符。
#include包含一個源代碼文件
這個預處理指令,我想是見得最多的一個,簡單說一下,第一種方法是用尖括號把頭文件括起來。這種格式告訴預處理程序在編譯器自帶的或外部庫的頭文件中搜索被包含的頭文件。第二種方法是用雙引號把頭文件括起來。這種格式告訴預處理程序在當前被編譯的應用程序的源代碼文件中搜索被包含的頭文件,如果找不到,再搜索編譯器自帶的頭文件。采用兩種不同包含格式的理由在於,編譯器是安裝在公共子目錄下的,而被編譯的應用程序是在它們自己的私有子目錄下的。一個應用程序既包含編譯器提供的公共頭文件,也包含自定義的私有頭文件。采用兩種不同的包含格式使得編譯器能夠在很多頭文件中區別出一組公共的頭文件。
#define定義宏
有關#define這個宏定義,在C語言中使用的很多,因為#define存在一些不足,C++強調使用const來定義常量。宏定義了一個代表特定內容的標識符。預處理過程會把源代碼中出現的宏標識符替換成宏定義時的值。記住僅僅是進行標識符的替換。下面列舉一些#define的使用:
1. 用#define實現求最大值和最小值的宏
1 #include <stdio.h> 2 #define MAX(x,y) (((x)>(y))?(x):(y)) 3 #define MIN(x,y) (((x)<(y))?(x):(y)) 4 int main(void) 5 { 6 #ifdef MAX //判斷這個宏是否被定義 7 printf("3 and 5 the max is:%d\n",MAX(3,5)); 8 #endif 9 #ifdef MIN 10 printf("3 and 5 the min is:%d\n",MIN(3,5)); 11 #endif 12 return 0; 13 } 14 15 /* 16 * (1)三元運算符要比if,else效率高 17 * (2)宏的使用一定要細心,需要把參數小心的用括號括起來, 18 * 因為宏只是簡單的文本替換,不注意,容易引起歧義錯誤。 19 */
2. 宏定義的錯誤使用
1 #include <stdio.h> 2 #define SQR(x) (x*x) 3 int main(void) 4 { 5 int b=3; 6 #ifdef SQR//只需要宏名就可以了,不需要參數,有參數的話會警告 7 printf("a = %d\n",SQR(b+2)); 8 #endif 9 return 0; 10 } 11 12 /* 13 *首先說明,這個宏的定義是錯誤的。並沒有實現程序中的B+2的平方 14 * 預處理的時候,替換成如下的結果:b+2*b+2 15 * 正確的宏定義應該是:#define SQR(x) ((x)*(x)) 16 * 所以,盡量使用小括號,將參數括起來。 17 */
3. 宏參數的連接
1 #include <stdio.h> 2 #define STR(s) #s 3 #define CONS(a,b) (int)(a##e##b) 4 int main(void) 5 { 6 #ifdef STR 7 printf(STR(VCK)); 8 #endif 9 #ifdef CONS 10 printf("\n%d\n",CONS(2,3)); 11 #endif 12 return 0; 13 } 14 15 /* (絕大多數是使用不到這些的,使用到的話,查看手冊就可以了) 16 * 第一個宏,用#把參數轉化為一個字符串 17 * 第二個宏,用##把2個宏參數粘合在一起,及aeb,2e3也就是2000 18 */
4. 用宏得到一個字的高位或低位的字節
1 #include <stdio.h> 2 #define WORD_LO(xxx) ((byte)((word)(xxx) & 255)) 3 #define WORD_HI(xxx) ((byte)((word)(xxx) >> 8)) 4 int main(void) 5 { 6 return 0; 7 } 8 9 /* 10 * 一個字2個字節,獲得低字節(低8位),與255(0000,0000,1111,1111)按位相與 11 * 獲得高字節(高8位),右移8位即可。 12 */
5. 用宏定義得到一個數組所含元素的個數
1 #include <stdio.h> 2 #define ARR_SIZE(a) (sizeof((a))/sizeof((a[0]))) 3 int main(void) 4 { 5 int array[100]; 6 #ifdef ARR_SIZE 7 printf("array has %d items.\n",ARR_SIZE(array)); 8 #endif 9 return 0; 10 } 11 /* 12 *總的大小除以每個類型的大小 13 */
關於#define宏的使用,應該特別小心,尤其是含有參數計算的時候如小2示例,最保險的做法將參數用括號括起來。
#ifdef,#ifndef,#endif...的使用
以上這些預編譯指令,都是條件編譯指令,也就是說,將決定那些代碼被編譯,而哪些不被編譯。
1. 示例1
1 #include <stdio.h> 2 #include <stdlib.h> 3 #define DEBUG 4 int main(void) 5 { 6 int i = 0; 7 char c; 8 while(1) 9 { 10 i++; 11 c = getchar(); 12 if('\n' != c) 13 { 14 getchar(); 15 } 16 if('q' == c || 'Q' == c) 17 { 18 #ifdef DEBUG//判斷DEBUG是否被定義了 19 printf("We get:%c,about to exit.\n",c); 20 #endif 21 break; 22 } 23 else 24 { 25 printf("i = %d",i); 26 #ifdef DEBUG 27 printf(",we get:%c",c); 28 #endif 29 printf("\n"); 30 } 31 } 32 printf("Hello World!\n"); 33 return 0; 34 } 35 36 /*#endif用於終止#if預處理指令。*/
2. ifdef 和 #ifndef
1 #include <stdio.h> 2 #define DEBUG 3 main() 4 { 5 #ifdef DEBUG 6 printf("yes "); 7 #endif 8 #ifndef DEBUG 9 printf("no "); 10 #endif 11 } 12 //#ifdefined等價於#ifdef; 13 //#if!defined等價於#ifndef
3. #else指令
4. #elif指令
5. 其他一些指令
1 #error指令將使編譯器顯示一條錯誤信息,然后停止編譯。 2 #line指令可以改變編譯器用來指出警告和錯誤信息的文件號和行號。 3 #pragma指令沒有正式的定義。編譯器可以自定義其用途。典型的用法是禁止或允許某些煩人的警告信息。
小結:
預處理就是在進行編譯的第一遍詞法掃描和語法分析之前所作的工作。說白了,就是對源文件進行編譯前,先對預處理部分進行處理,然后對處理后的代碼進行編譯。這樣做的好處是,經過處理后的代碼,將會變的很精短。