預處理命令和前處理器(CPP),C語言預處理命令和前處理器概述

2020-07-16 10:04:22
C 語言程式設計過程中,經常會用到如 #include、#define 等指令,這些標識開頭的指令被稱為預處理指令,預處理指令由預處理程式(前處理器)操作。較之其他程式語言,C/C++ 語言更依賴前處理器,故在閱讀或開發 C/C++ 程式過程中,可能會接觸大量的預處理指令。

預處理指令及分類

C/C++ 程式中的原始碼中包含以 # 開頭的各種編譯指令,這些指令稱為預處理指令。預處理指令不屬於 C/C++ 語言的語法,但在一定意義上可以說預處理擴充套件了 C/C++。

ANSI C 定義的預處理指令主要包括:檔案包含宏定義條件編譯特殊控制等 4 類。

1) 檔案包含:#include 是 C 程式設計中最常用的預處理指令。例如,幾乎每個需要輸入輸出的 C 程式,都要包含 #include<stdio.h> 指令,表示把 stdio.h 檔案中的全部內容,替換該行指令。

包含檔案的格式有 #include 後面跟尖括號 <> 和雙引號 "" 之分。兩者的主要差別是搜尋路徑的不同。
  • 尖括號形式:如 #include<math.h>,前處理器直接到系統目錄對應檔案中搜尋 math.h 檔案,搜尋不到則報錯。系統提供的標頭檔案一般採用該包含方式,而自定義的標頭檔案不能採用該方式。
  • 雙引號形式:如 #include"cal.h",首先到當前工作目錄下查詢該檔案,如果沒有找到,再到系統目錄下查詢。包含自定義的標頭檔案,一般採用該方式。雖然系統標頭檔案採用此方式也正確,但浪費了不必要的搜尋時間,故系統標頭檔案不建議採用該包含方式。

2) 宏定義:包括定義宏 #define 和宏刪除 #undef。

以 #define 開頭,可以定義無引數宏和帶參的宏定義。程式中經常使用無參宏定義來定義符號常數。例如:
#define PI 3.1416 //定義無符號宏,或定義符號常數 PI
#undef 表示刪除已定義的宏,例如:
#undef PI //刪除前面該宏的定義
3) 條件編譯:主要是為了有選擇性地執行相應操作,防止宏替換內容(如檔案等)的重複包含。常見的條件編譯指令有 #if、#elif、#else、#endif、#ifdef、#ifndef。

4) 特殊控制:ANSI C 還定義了特殊作用的預處理指令,如 #error、#pragma。

#error:使前處理器輸出指定的錯誤資訊,通常用於偵錯程式。

#pragma:是功能比較豐富且靈活的指令,可以有不同的引數選擇,從而完成相應的特 定功能操作。呼叫格式為:#pragma 引數。

其中,引數可以有 message 型別、code_seg、once、warning、pack 等。通常使用如下的預處理指令來設定記憶體以 n 位元組對齊方式。

#pragma pack (n) //其中 n 稱為對齊系數,取 1、2、4、8...

前處理器及其工作原理

C前處理器(C Pre-Processor)也常簡寫為 CPP,是一個與 C 編譯器獨立的小程式,預編譯器並不理解 C 語言語法,它僅是在程式原始檔被編譯之前,實現文字替換的功能。

目前預編譯器巳整合到整合式開發環境中,一般並沒有執行預處理操作的選項,而包含在了編譯操作中,即選擇編譯操作時,首先呼叫的是前處理器,處理源程式檔案中的預處理指令,前處理器的輸出再送給編譯器,編譯器從 C 語言語法角度檢查程式是否正確,如果正確,則生成目的碼檔案或機器指令檔案。

C 前處理器及 C 編譯器的執行順序及輸入輸出檔案型別,如圖 1 所示。

圖 1 預處理與編譯