1. 程式人生 > >C語言__預處理(巨集定義、檔案包含、條件編譯)

C語言__預處理(巨集定義、檔案包含、條件編譯)

C語言__預處理(巨集定義、檔案包含、條件編譯)

預處理簡單理解

    1.C語言在對源程式進行編譯之前,會先對一些特殊的預處理指令作解釋(比如之前使用的#include檔案包含指令),產生一個新的源程式(這個過程稱為編譯預處理),之後再進行通常的編譯

    2.為了區分預處理指令和一般的C語句,所有預處理指令都以符號"#"開頭,並且結尾不用分號

    3.預處理指令可以出現在程式的任何位置,它的作用範圍是從它出現的位置到檔案尾。習慣上我們儘可能將預處理指令寫在源程式開頭,這種情況下,它的作用範圍就是整個源程式檔案

    4.C語言提供的預處理指令主要有:巨集定義、檔案包含、條件編譯

巨集定義分類——不帶引數的巨集定義 和 帶引數的巨集定義

——不帶引數的巨集定義

    1.一般形式:#define 巨集名 字串 【例】 #define ABC 10

    2.作用:在編譯預處理時,將源程式中所有"巨集名"替換成右邊的"字串",常用來定義常量。

    3.使用習慣和注意:巨集名一般用大寫字母,以便與變數名區別開來,但用小寫也沒有語法錯誤;對程式中用雙引號擴起來的字串內的字元,不進行巨集的替換操作;在編譯預處理用字串替換巨集名時,不作語法檢查,只是簡單的字串替換。只有在編譯的時候才對已經展開巨集名的源程式進行語法檢查;巨集名的有效範圍是從定義位置到檔案結束。如果需要終止巨集定義的作用域,可以用#undef命令;定義一個巨集時可以引用已經定義的巨集名。

#define PI 3.14
#define R  3.0
#define L  2*PI*R
#define S  PI*R*R
/*
作用範圍
/*
#undef PI
/*
之後PI不在進行字串替換
*/
——帶引數的巨集定義

1.一般形式:#define 巨集名(引數列表) 字串

2.作用:在編譯預處理時,將源程式中所有巨集名替換成字串,並且將 字串中的引數 用 巨集名右邊引數列表 中的引數替換

3.使用習慣和注意:巨集名和引數列表之間不能有空格,否則空格後面的所有字串都作為替換的字串;帶引數的巨集在展開時,只作簡單的字元和引數的替換,要想計算得確保計算在巨集定義中書寫正確。

#include <stdio.h>

#define Pow(a) (a) * (a)
#define Pow_1(a) ( (a) * (a) )

int main(int argc, const char * argv[]) {
    int b = Pow(10) / Pow(2);  //  int b = (10) * (10) / (2) * (2);
    int b = Pow_1(10) / Pow(2);  //  int b = ( (10) * (10) ) / ( (2) * (2) );
    
    printf("%d", b);
    return 0;
}

巨集定義和函式的區別

    1.巨集定義不涉及儲存空間的分配、引數型別匹配、引數傳遞、返回值問題
    2. 函式呼叫在程式執行時執行,而巨集替換隻在編譯預處理階段進行。所以帶引數的巨集比函式具有更高的執行效率

條件編譯的概念——在很多情況下,我們希望程式的其中一部分程式碼只有在滿足一定條件時才進行編譯,否則不參與編譯(只有參與編譯的程式碼最終才能被執行)。

    1.基本用法

#if 條件1  //  如果條件1成立,那麼編譯器就會把#if 與 #elif之間的code1程式碼編譯進去
           //(注意:是編譯進去,不是執行,很平時用的if-else是不一樣的)
 ...code1...
#elif 條件2  //  如果條件1不成立、條件2成立,那麼編譯器就會把#elif 與 #else之間的code2程式碼編譯進去
 ...code2...
#else  //  如果條件1、2都不成立,那麼編譯器就會把#else 與 #endif之間的code3編譯進去
 ...code3...
#endif  //  條件編譯結束後,要在最後面加一個#endif,不然後果很嚴重
注意:#if 和 #elif後面的條件一般是判斷巨集定義而不是判斷變數,因為條件編譯是在編譯之前做的判斷,巨集定義也是編譯之前定義的,而變數是在執行時才有使用的意義。

    2.其他用法

#if defined(MAX)  //  如果前面已經定義過MAX這個巨集,就將code編譯進去。
    ...code...
#endif

#if !defined(MAX)  // 如果前面沒有定義過MAX這個巨集,就將code編譯進去。
     ...code...
#endif

            //  #ifdef的使用和#if defined()的用法基本一致
#ifdef MAX  //  如果前面已經定義過MAX這個巨集,就將code編譯進去。  
    ...code...
#endif

             //  #ifndef又和#if !defined()的用法基本一致
#ifndef MAX  //  如果前面沒有定義過MAX這個巨集,就將code編譯進去。
    ...code...
#endif

檔案包含——#include,它可以將一個檔案的全部內容拷貝另一個檔案中。

    1.一般形式:

                        第1種形式#include <檔名>
                        第2種形式 #include "檔名"

    2.使用注意:

#include指令允許巢狀包含,比如a.h包含b.h,b.h包含c.h,但是不允許遞迴包含,比如 a.h 包含 b.h,b.h 包含 a.h。

使用#include指令可能導致多次包含同一個標頭檔案,降低編譯效率。防止多次包含同一標頭檔案導致效率降低,使用條件編譯。

#ifndef _ONE_H_  //  條件編譯第一次執行  檢查_ONE_H_  是否被巨集定義,沒有就順序執行;已經定義了就可以直接跳過
#define _ONE_H_  //  進行巨集定義_ONE_H_  

void one();      //  宣告函式

#endif           //  結束本次的條件編譯