title
date
br#description
c語言編譯預處理
2019-11-06 14:35:32 -0800
預處理
c語言
c語言預處理是對源**進行初步轉換
**碼農有道
c源程式->編譯預處理->編譯->優化程式->匯程式設計序->鏈結程式->可執行檔案
其中編譯預處理階段,讀取c源程式,對其中的預處理指令(以#開頭的指令)和特殊符號進行處理。或者說是掃瞄源**,對其進行初步的轉換,產生新的源**提供給編譯器。
預處理過程先於編譯器對源**進行處理,讀入源**,檢查包含預處理指令的語句和巨集定義,並對源**進行轉換。預處理過程還會刪除程式中的注釋和多餘的空白字元。
在c語言的程式中可包括各種以符號#開頭的編譯指令,這些指令稱為預處理命令。預處理命令屬於c語言編譯器,而不是c語言的組成部分。通過預處理命令可擴充套件c語言程式設計的環境。
預處理指令是以#號開頭的**行。#號必須是該行除了任何空白字元外的第乙個字元。#後是指令關鍵字,在關鍵字和#號之間允許存在任意個數的空白字元。整行語句構成了一條預處理指令,該指令將在編譯器進行編譯之前對源**做某些轉換。
當乙個c語言程式由多個檔案模組組成時,主模組中一般包含main函式和一些當前程式專用的函式。程式從main函式開始執行,在執行過程中,可呼叫當前檔案中的函式,也可呼叫其他檔案模組中的函式。
如果在模組中要呼叫其他檔案模組中的函式,首先必須在主模組中宣告該函式原型。一般都是採用檔案包含的方法,包含其他檔案模組的標頭檔案。
檔案包含中指定的檔名即可以用引號括起來,也可以用尖括號括起來,格式如下:
#include 《檔名》
//或#include "檔名"
如果使用尖括號<>括起檔名,則編譯程式將到c語言開發環境中設定好的 include檔案中去找指定的檔案。
因為c語言的標準標頭檔案都存放在include資料夾中,所以一般對標準標頭檔案採用尖括號;對程式設計自己編寫的檔案,則使用雙引號。
如果自己編寫的檔案不是存放在當前工作資料夾,可以在#include命令後面加在路徑。
#include
命令的作用是把指定的檔案模組內容插入到#include所在的位置,當程式編譯鏈結時,系統會把所有#include指定的檔案鏈結生成可執行**。檔案包含必須以#開頭,表示這是編譯預處理命令,行尾不能用分號結束。
#include
所包含的檔案,其副檔名可以是「.c」,表示包含普通c語言源程式。也可以是 「.h」,表示c語言程式的標頭檔案。c語言系統中大量的定義與宣告是以標頭檔案形式提供的。
使用#define命令並不是真正的定義符號常量,而是定義乙個可以替換的巨集。被定義為巨集的識別符號稱為「巨集名」。在編譯預處理過程時,對程式中所有出現的「巨集名」,都用巨集定義中的字串去代換,這稱為「巨集替換」或「巨集展開」。
在c語言中,巨集分為有引數和無引數兩種。
無引數的巨集
其定義格式如下:
#define 巨集名 字串
在以上巨集定義語句中,各部分的含義如下:
#
表示這是一條預處理命令(凡是以「#」開始的均為預處理命令)。 define 關鍵字「define」為巨集定義命令。 巨集名 是乙個標示符,必須符合c語言標示符的規定,一般以大寫字母標識巨集名。 字串 可以是常數,表示式,格式串等。在前面使用的符號常量的定義就是乙個無引數巨集定義。
注意:預處理命令語句後面一般不會新增分號,如果在#define最後有分號,在巨集替換時分號也將替換到源**中去。在巨集名和字串之間可以有任意個空格。
#define pi 3.141592
#include #define pi 3.14
int main()
執行預編譯指令gcc -e -o pre1.e pre1.c
,得到預處理pre1.e
檔案,如下:
# 5 "pre1.c"
int main()
可見pi
被替換為了3.14
巨集定義是巨集名來表示乙個字串,在巨集展開時又以該字串取代巨集名。這只是一種簡單的代換,字串中可以含任何字元,可以是常數,也可以是表示式,預處理程式對它不作任何檢查。如有錯誤,只能在編譯已被巨集展開後的源程式時發現。
巨集定義允許巢狀,在巨集定義的字串中可以使用已經定義的巨集名。在巨集展開時由預處理程式層層替換。
習慣上巨集名用大寫字母表示,以方便與變數區別。但也可以用小寫字母。
帶引數的巨集
#define
命令定義巨集時,還可以為巨集設定引數。與函式中的引數類似,在巨集定義中的引數為形式引數,在巨集呼叫中的引數稱為實際引數。對帶引數的巨集,在呼叫中,不僅要巨集展開,還要用實參去代換形參。
帶參巨集定義的一般形式為:
#define 巨集名(形參表) 字串
在定義帶引數的巨集時,巨集名和形參表之間不能有空格出現,否則,就將巨集定義成為無引數形式,而導致程式出錯。
#define max(x,y) ((x)>(y)?(x):(y))
以上的巨集定義中,如果x的值大於y,得到x,否則得到y。
#include #define max(a,b) ((a>b)?a:b)
int main()
執行gcc -e -o pre2.e pre2.c並檢視
# 5 "pre2.c"
int main()
帶參的巨集和帶參的函式相似,但其本質是不同的。使用帶參巨集時,在預處理時將程式源**替換到相應的位置,編譯時得到完整的目標**,而不進行函式呼叫,因此程式執行效率要高些。而函式呼叫只需要編譯一次函式,**量較少,一般情況下,對於簡單的功能,可使用巨集替換的形式來使用。
帶引數的巨集不容易理解,所以,在實際開發中,我不建議使用帶引數的巨集。
條件編譯有多種格式,在這裡我只介紹最常用的兩種格式#ifdef和#ifndef。
#ifdef
#ifdef 識別符號
程式段 1
#else
程式段 2
#endif
其意義是,如果#ifdef後面的識別符號已被定義過,則對「程式段1」進行編譯;如果沒有定義識別符號,則編譯「程式段2」。一般不使用#else及後面的「程式2」。
#include #define linux
int main()
# 5 "pre3.c"
int main()
#ifndef
而#ifndef的意義與#ifdef相反,其格式如下:
#ifndef 識別符號
程式段 1
#else
程式段 2
#endif
其意義是,如果未定義識別符號,則編譯「程式段1」;否則編譯「程式段2」
在實際開發中,程式設計師用#ifndef來防止標頭檔案被重複包含。
開啟/usr/include/stdio.h
,第一條有效行的**是#ifndef _stdio_h
,接下來是#define _stdio_h 1
,最後一行是#endif
。
程式設計師自定義的標頭檔案,我們也會這麼寫,如:
如果標頭檔案被包含多次,就表示標頭檔案中的函式被多次宣告,全域性變數被多次定義,在以前的c語言編譯器中,這是不允許的,編譯時會報錯,但是,現在的部分編譯器比較智慧型,多次定義全域性變數或多次宣告函式也不會報錯。這些新的特徵讓我這個老傢伙很不適應。
#undef
#undef
取消已定義的識別符號。
C語言 08 編譯預處理
8.1.1 無引數的巨集定義 巨集常量 如果在程式中大量使用到了100這個值,那麼為了方便管理,我們可以將其定義為 const int num 100 但是如果我們使用num定義乙個陣列,在不支援c99標準的編譯器上是不支援的,因為num不是乙個編譯器常量,如果想得到了乙個編譯器常量,那麼可以使用 ...
c語言整理編譯預處理
c程式執行過程 源程式 編譯預處理 編譯 優化程式 匯程式設計序 鏈結程式 可執行檔案。編譯預處理時,先要讀取源程式,對預處理指令 開頭指令 以及特殊的符號進行處理,比如define 替換指令,也會進行刪除注釋,多餘的空白字元,然後產生的預處理檔案或者程式傳給編譯器,在程式中以 開頭的編譯指令稱為預...
C 編譯預處理
c 的預處理是編譯器在編譯源程式之前,先由預處理器處理預處理指令,由於在c 源程式中有各種編譯命令,而這些編譯命令由於是在程式被正常編譯之前執行的,故稱為預處理命令 或指令 預編譯命令用來擴充c 程式設計的環境,使得程式書寫變得更加簡練和清晰。c 提供的預處理功能主要有以下3種 巨集定義命令。檔案包...