1. 程式人生 > >轉載至C語言中文網-跨平臺條件編譯

轉載至C語言中文網-跨平臺條件編譯

假如現在要開發一個C語言程式,讓它輸出紅色的文字,並且要求跨平臺,在 Windows 和 Linux 下都能執行,怎麼辦呢?

這個程式的難點在於,不同平臺下控制文字顏色的程式碼不一樣,我們必須要能夠識別出不同的平臺。

Windows 有專有的巨集_WIN32,Linux 有專有的巨集__linux__,以現有的知識,我們很容易就想到了 if else,請看下面的程式碼:
  1. #include <stdio.h>
  2. int main(){
  3. if(_WIN32){
  4. system("color 0c");
  5. printf("http://c.biancheng.net
    \n");
  6. }else if(__linux__){
  7. printf("\033[22;31mhttp://c.biancheng.net\n\033[22;30m");
  8. }else{
  9. printf("http://c.biancheng.net\n");
  10. }
  11. return 0;
  12. }
但這段程式碼是錯誤的,在 Windows 下提示 __linux__ 是未定義的識別符號,在 Linux 下提示 _Win32 是未定義的識別符號。對上面的程式碼進行改進:
  1. #include <stdio.h>
  2. int main(){
  3. #if _WIN32
  4. system
    ("color 0c");
  5. printf("http://c.biancheng.net\n");
  6. #elif __linux__
  7. printf("\033[22;31mhttp://c.biancheng.net\n\033[22;30m");
  8. #else
  9. printf("http://c.biancheng.net\n");
  10. #endif
  11. return 0;
  12. }
#if、#elif、#else 和 #endif 都是預處理命令,整段程式碼的意思是:如果巨集 _WIN32 的值為真,就保留第 4、5 行程式碼,刪除第 7、9 行程式碼;如果巨集 __linux__ 的值為真,就保留第 7 行程式碼;如果所有的巨集都為假,就保留第 9 行程式碼。


這些操作都是在預處理階段完成的,多餘的程式碼以及所有的巨集都不會參與編譯,不僅保證了程式碼的正確性,還減小了編譯後文件的體積。

這種能夠根據不同情況編譯不同程式碼、產生不同目標檔案的機制,稱為條件編譯。條件編譯是預處理程式的功能,不是編譯器的功能。

條件編譯有多種形式,下面一一講解。

#if 命令

#if 命令的完整格式為:

#if 整型常量表達式1
    程式段1
#elif 整型常量表達式2
    程式段2
#elif 整型常量表達式3
    程式段3
#else
    程式段4
#endif

它的意思是:如常“表示式1”的值為真(非0),就對“程式段1”進行編譯,否則就計算“表示式2”,結果為真的話就對“程式段2”進行編譯,為假的話就繼續往下匹配,直到遇到值為真的表示式,或者遇到 #else。這一點和 if else 非常類似。

需要注意的是,#if 命令要求判斷條件為“整型常量表達式”,也就是說,表示式中不能包含變數,而且結果必須是整數;而 if 後面的表示式沒有限制,只要符合語法就行。這是 #if 和 if 的一個重要區別。

#elif 和 #else 也可以省略,如下所示:
  1. #include <stdio.h>
  2. int main(){
  3. #if _WIN32
  4. printf("This is Windows!\n");
  5. #else
  6. printf("Unknown platform!\n");
  7. #endif
  8. #if __linux__
  9. printf("This is Linux!\n");
  10. #endif
  11. return 0;
  12. }

#ifdef 命令

#ifdef 命令的格式為:

#ifdef  巨集名
    程式段1
#else
    程式段2
#endif

它的意思是,如果當前的巨集已被定義過,則對“程式段1”進行編譯,否則對“程式段2”進行編譯。

也可以省略 #else:

#ifdef  巨集名
    程式段
#endif


VS/VC 有兩種編譯模式,Debug 和 Release。在學習過程中,我們通常使用 Debug 模式,這樣便於程式的除錯;而最終釋出的程式,要使用 Release 模式,這樣編譯器會進行很多優化,提高程式執行效率,刪除冗餘資訊。

為了能夠清楚地看到當前程式的編譯模式,我們不妨在程式中增加提示,請看下面的程式碼:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(){
  4. #ifdef _DEBUG
  5. printf("正在使用 Debug 模式編譯程式...\n");
  6. #else
  7. printf("正在使用 Release 模式編譯程式...\n");
  8. #endif
  9. system("pause");
  10. return 0;
  11. }
當以 Debug 模式編譯程式時,巨集 _DEBUG 會被定義,預處器會保留第 5 行程式碼,刪除第 7 行程式碼。反之會刪除第 5 行,保留第 7 行。

#ifndef 命令

#ifndef 命令的格式為:

#ifndef 巨集名
    程式段1 
#else 
    程式段2 
#endif

與 #ifdef 相比,僅僅是將 #ifdef 改為了 #ifndef。它的意思是,如果當前的巨集未被定義,則對“程式段1”進行編譯,否則對“程式段2”進行編譯,這與 #ifdef 的功能正好相反。

區別

最後需要注意的是,#if 後面跟的是“整型常量表達式”,而 #ifdef 和 #ifndef 後面跟的只能是一個巨集名,不能是其他的。

例如,下面的形式只能用於 #if:
  1. #include <stdio.h>
  2. #define NUM 10
  3. int main(){
  4. #if NUM == 10 || NUM == 20
  5. printf("NUM: %d\n", NUM);
  6. #else
  7. printf("NUM Error\n");
  8. #endif
  9. return 0;
  10. }
執行結果:
NUM: 10

再如,兩個巨集都存在時編譯程式碼A,否則編譯程式碼B:
  1. #include <stdio.h>
  2. #define NUM1 10
  3. #define NUM2 20
  4. int main(){
  5. #if (definedNUM1 && definedNUM2)
  6. //程式碼A
  7. printf("NUM1: %d, NUM2: %d\n", NUM1, NUM2);
  8. #else
  9. //程式碼B
  10. printf("Error\n");
  11. #endif
  12. return 0;
  13. }
執行結果:
NUM1: 10, NUM2: 20

#ifdef 可以認為是 #if defined 的縮寫。

部分預處理語句

指令

說明

#

空指令,無任何效果

#include

包含一個原始碼檔案

#define

定義巨集

#undef

取消已定義的巨集

#if

如果給定條件為真,則編譯下面程式碼

#ifdef

如果巨集已經定義,則編譯下面程式碼

#ifndef

如果巨集沒有定義,則編譯下面程式碼

#elif

如果前面的#if給定條件不為真,當前條件為真,則編譯下面程式碼

#endif

結束一個#if……#else條件編譯塊部分預處理語句