摘要:在C/C++語言編程過程中,經(jīng)常會(huì)用到如#include、#define等指令,同時(shí)也會(huì)涉及到大量的預(yù)處理與條件編譯,這樣做的好處可以使代碼更利于移植移植性,也讓代碼易于修改。 因此引入了預(yù)處理與條件編譯的概念。
預(yù)處理的行為是由指令控制的。 所有的預(yù)處理器命令都是以#開頭,它必須是第一個(gè)非空字符。 預(yù)處理指令由預(yù)處理程序(預(yù)處理器)操作。
預(yù)處理器不是編譯器的組成部分,但是它是編譯過程中一個(gè)單獨(dú)的步驟。 因此, 預(yù)處理器只不過是一個(gè)文本替換工具而已,它們會(huì)指示編譯器在實(shí)際編譯之前完成所需的預(yù)處理。 通俗來講預(yù)處理命令的作用就是在編譯和鏈接之前,對(duì)源文件進(jìn)行一些文本方面的操作,比如文本替換、文件包含、刪除部分代碼等,這個(gè)過程叫做預(yù)處理(在編譯之前對(duì)源文件進(jìn)行簡單加工)
相比其他編程語言,C/C++語言更依賴預(yù)處理器,故在閱讀或開發(fā)C/C++程序過程中,可能會(huì)接觸大量的預(yù)處理指令。 預(yù)處理指令不屬于C/C++語言的語法,但在一定意義上可以說預(yù)處理擴(kuò)展了C/C++。 預(yù)處理命令的分類主要?jiǎng)澐譃橐韵聨追N類型:
1、宏定義
#define命令并不是真正的定義符號(hào)常量,而是定義一個(gè)可以替換的宏。 被定義為宏的標(biāo)示符稱為“宏名”。 在編譯預(yù)處理過程時(shí),對(duì)程序中所有出現(xiàn)的“宏名”,都用宏定義中的字符串去代換,這稱為“宏代換”或“宏展開”。 宏替換在編譯前進(jìn)行,不分配內(nèi)存; 宏展開不占運(yùn)行時(shí)間,只占編譯時(shí)間; 宏替換只作替換,不做計(jì)算。
#define NEMBER 9 //#define 宏名 文本
#define M(a, b) a*b //#define 宏名(參數(shù)表) 文本
#define SWITCHON //#define 宏名 //(定義一個(gè)條件編譯的開關(guān)字段)
#define NAME(n) num ## n //宏定義,使用 ## 運(yùn)算符,粘合的作用
int num0 = 10;
printf("num0 = %d\\n", NAME(0));//宏調(diào)用NAME(0)被替換為 num ## 0,被粘合為:num0。
//可變宏:… 和 __VA_ARGS__
#define PR(...) printf(__VA_ARGS__) //宏定義
PR("hello\\n"); //宏調(diào)用
//輸出結(jié)果:hello
//在宏定義中,形參列表的最后一個(gè)參數(shù)為省略號(hào)“…”,而“__VA_ARGS__”就可以被用在替換文本中,來表示省略號(hào)“…”代表了什么。
//而上面例子宏代換之后為:printf(“hello\\n”);
#undef指令刪除前面定義的宏名字(也就是#define的標(biāo)識(shí)符)。 也就是說,它“不定義”宏。 (注意:如果標(biāo)識(shí)符當(dāng)前沒有被定義成一個(gè)宏名稱,那么就會(huì)忽略該指令),一般形式為:
#undef NEMBER //取消之前已定義的NEMBER
#define NEMBER 100 //重新定義NUMBE為100
2、系統(tǒng)預(yù)定義的宏
LINE : 當(dāng)前源文件的行號(hào),整數(shù)
FILE : 當(dāng)前源文件名,char 字符串,文件的完整路徑和文件名**
DATE : 當(dāng)前編譯日期,char 字符串,格式:月 日 年
TIME : 當(dāng)前編譯時(shí)間,char 字符串,格式:時(shí) 分 秒
STDC : 整數(shù) 1,表示兼容 ANSI/ISO C 標(biāo)準(zhǔn),配合 #if 使用
**TIMESTAMP ** : 最后一次修改當(dāng)前文件的時(shí)間戳,char 字符串,格式:年 月份 日期 時(shí) 分 秒
3、文件包含
當(dāng)一個(gè)C語言程序由多個(gè)文件模塊組成時(shí),主模塊中一般包含main函數(shù)和一些當(dāng)前程序?qū)S玫暮瘮?shù)。 程序從main函數(shù)開始執(zhí)行,在執(zhí)行過程中,可調(diào)用當(dāng)前文件中的函數(shù),也可調(diào)用其他文件模塊中的函數(shù)。
如果在模塊中要調(diào)用其他文件模塊中的函數(shù),首先必須在主模塊中聲明該函數(shù)原型。 一般都是采用文件包含的方法,包含其他文件模塊的頭文件。
包含文件的格式有#include后面跟尖括號(hào)<>和雙引號(hào)“”之分。 兩者的主要差別是搜索路徑的不同。 C的標(biāo)準(zhǔn)庫加.h,C++標(biāo)準(zhǔn)庫可以不加.h。
尖括號(hào)形式:如#include
雙引號(hào)形式:如#include“para.h”,首先到當(dāng)前工作目錄下查找該文件,如果未發(fā)現(xiàn),再按尖括號(hào)包含時(shí)的辦法到系統(tǒng)目錄下查找。 包含自定義的頭文件,一般采用該方式。 雖然系統(tǒng)標(biāo)準(zhǔn)庫頭文件采用此方式也正確,但浪費(fèi)了不必要的搜索時(shí)間,故系統(tǒng)標(biāo)準(zhǔn)庫頭文件不建議采用該包含方式。
4、條件編譯
條件編譯允許程序員有選擇按照不同的條件去編譯程序的不同部分,從而得到不同的目標(biāo)代碼。 使用條件編譯,可方便地處理程序的調(diào)試版本和正式版本,也可使用條件編譯使程序的移植更方便。
常見的條件編譯指令有 #if、#elif、#else、#endif、#ifdef、#ifndef。
#if、#elif、#else、#endif的使用和if、elseif 、else的使用非常相似,一般使用格式如下:
#if 整型常量表達(dá)式1
程序段1
#elif 整型常量表達(dá)式2
程序段2
#else
程序段3
#endif
執(zhí)行起來就是,如果整形常量表達(dá)式為真,則執(zhí)行程序段1,否則繼續(xù)往后判斷依次類推(注意是整形常量表達(dá)式),最后#endif是#if的結(jié)束標(biāo)志。
#ifdef的作用是判斷某個(gè)宏是否定義,如果該宏已經(jīng)定義則執(zhí)行后面的代碼,#ifndef恰好和#ifdef相反,一般使用格式如下:
//ifdef
#ifdef 宏名
程序段1
#else
程序段2
#endif
//ifndef
#ifndef 宏名
程序段1
#else
程序段2
#endif
#ifdef表示如果該宏已被定義過,則對(duì)“程序段1”進(jìn)行編譯,否則對(duì)“程序段2”進(jìn)行編譯(這個(gè)和上面的#if一樣最后都需要#endif),上述格式也可以不用#else,這一點(diǎn)上和if else相同。
#ifndef表示如果該宏未被定義,則對(duì)“程序段1”進(jìn)行編譯,否則對(duì)“程序段2”進(jìn)行編譯。
5、特殊命令
#line 可以改變 LINE 和 _FILE_兩個(gè)宏的內(nèi)容,即為其指定新的值。 其本質(zhì)是重定義 LINE 和 FILE ,主要有以下兩種形式:
#line linenum
#line linenum 文件名
int main()
{
printf( "code is on line %d, in file %s\\n", __LINE__, __FILE__ );
#line 10
printf( "code is on line %d, in file %s\\n", __LINE__, __FILE__ );
#line 20 "hello.cpp"
printf( "code is on line %d, in file %s\\n", __LINE__, __FILE__ );
printf( "code is on line %d, in file %s\\n", __LINE__, __FILE__ );
}
輸出為:
code is on line 7, in file line_directive.cpp
code is on line 10, in file line_directive.cpp
code is on line 20, in file hello.cpp
code is on line 21, in file hello.cpp
#error :當(dāng)預(yù)處理器預(yù)處理到#error命令時(shí)將停止編譯并輸出用戶自定義的錯(cuò)誤消息,一般用于調(diào)試程序。
#error [用戶自定義的錯(cuò)誤消息]
//注:上述語法成份中的方括號(hào)"[]"代表用戶自定義的錯(cuò)誤消息可以省略不寫。
//舉例1:
#error Sorry,an error has occurred!
//舉例2:
#error
#ifndef A
#define A 5
#endif
#if A < 5
#error Sorry,an error has occurred!
#endif
#warning :****類似于#error 指令,但不會(huì)導(dǎo)致取消預(yù)處理,程序繼續(xù)編譯,不會(huì)影響程序的正常運(yùn)行。 #warning 指令之后的信息在預(yù)處理繼續(xù)之前作為消息輸出,產(chǎn)生警告。
#warning [用戶自定義的警告信息]
#warning Sorry,an warning has occurred!
#pragma:是功能比較豐富且靈活的指令,可以有不同的參數(shù)選擇,從而完成相應(yīng)的特 定功能操作。 #pragma指令是計(jì)算機(jī)或操作系統(tǒng)特定的,并且通常對(duì)于每個(gè)編譯器而言都有所不同。 #pragma指令可用于條件語句以提供新的預(yù)處理器功能,或?yàn)榫幾g器提供實(shí)現(xiàn)所定義的信息
其格式一般為: #pragma Para。 其中Para 為參數(shù),參數(shù)可以有 message 類型、code_seg、once、warning、pack 等,具體可以在網(wǎng)上詳細(xì)查看。 舉兩個(gè)常用的例子:
#pragma一次
只要在頭文件的最開始加入這條指令就能夠保證指定該文件在編譯源代碼文件時(shí)僅由編譯器包含(打開)一次,使用 #pragma once 可減少生成次數(shù),和使用預(yù)處理宏定義來避免多次包含文件的內(nèi)容的效果是一樣的,但是需要鍵入的代碼少,可減少錯(cuò)誤率,這條指令實(shí)際上在VC6中就已經(jīng)有了,但是考慮到兼容性并沒有太多的使用它。
//使用#progma once
#pragma once
// Code placed here is included only once per translation unit
//使用宏定義方式
#ifndef HEADER_H_
#define HEADER_H_
// Code placed here is included only once per translation unit
#endif // HEADER_H_
#pragma once是編譯相關(guān),就是說這個(gè)編譯系統(tǒng)上能用,但在其他編譯系統(tǒng)不一定可以,也就是說移植性差,不過基本上已經(jīng)是每個(gè)編譯器都有這個(gè)定義了。
#pragma包 (n)
指定結(jié)構(gòu)、聯(lián)合和類成員的封裝對(duì)齊。 其實(shí)就是改變編譯器的內(nèi)存對(duì)齊方式。 這個(gè)功能對(duì)于集合數(shù)據(jù)體使用,默認(rèn)的數(shù)據(jù)的對(duì)齊方式占用內(nèi)存比較大,可進(jìn)行修改。 在沒有參數(shù)的情況下調(diào)用pack會(huì)將n設(shè)置為編譯器選項(xiàng)中設(shè)置的值。 如果未設(shè)置編譯器選項(xiàng),windows默認(rèn)為8,linux默認(rèn)為4。 具體的使用方法為,其中n稱為對(duì)齊系數(shù),取值必須是2的冪次方,即1、2、4、8、16等。
1. #pragma pack(show) 以警告信息的形式顯示當(dāng)前字節(jié)對(duì)齊的值.
2. #pragma pack(n) 將當(dāng)前字節(jié)對(duì)齊值設(shè)為 n .
3. #pragma pack() 將當(dāng)前字節(jié)對(duì)齊值設(shè)為默認(rèn)值(通常是8) .
4. #pragma pack(push) 將當(dāng)前字節(jié)對(duì)齊值壓入編譯棧棧頂.
5. #pragma pack(pop) 將編譯棧棧頂?shù)淖止?jié)對(duì)齊值彈出并設(shè)為當(dāng)前值.
6. #pragma pack(push, n) 先將當(dāng)前字節(jié)對(duì)齊值壓入編譯棧棧頂, 然后再將 n 設(shè)為當(dāng)前值.
7. #pragma pack(pop, n) 將編譯棧棧頂?shù)淖止?jié)對(duì)齊值彈出, 然后丟棄, 再將 n 設(shè)為當(dāng)前值.
8. #pragma pack(push, identifier) 將當(dāng)前字節(jié)對(duì)齊值壓入編譯棧棧頂, 然后將棧中保存該值的位置標(biāo)識(shí)為 identifier .
10. #pragma pack(pop, identifier) 將編譯棧棧中標(biāo)識(shí)為 identifier 位置的值彈出, 并將其設(shè)為當(dāng)前值. 注意, 如果棧中所標(biāo)識(shí)的位置之上還有值, 那會(huì)先被彈出并丟棄.
11. #pragma pack(push, identifier, n) 將當(dāng)前字節(jié)對(duì)齊值壓入編譯棧棧頂, 然后將棧中保存該值的位置標(biāo)識(shí)為 identifier, 再將 n 設(shè)為當(dāng)前值.
12. #pragma pack(pop, identifier, n) 將編譯棧棧中標(biāo)識(shí)為 identifier 位置的值彈出, 然后丟棄, 再將 n 設(shè)為當(dāng)前值. 注意, 如果棧中所標(biāo)識(shí)的位置之上還有值, 那會(huì)先被彈出并丟棄.
//注意: 如果在棧中沒有找到 pop 中的標(biāo)識(shí)符, 則編譯器忽略該指令, 而且不會(huì)彈出任何值.
通常成對(duì)使用:
#pragma pack (n) //作用:編譯器將按照n個(gè)字節(jié)對(duì)齊。
#pragma pack () //作用:取消自定義字節(jié)對(duì)齊方式。
#pragma pack (push,1) //作用:是指把原來對(duì)齊方式設(shè)置壓棧,并設(shè)新的對(duì)齊方式設(shè)置為一個(gè)字節(jié)對(duì)齊
#pragma pack(pop) //作用:恢復(fù)對(duì)齊狀態(tài)
-
處理器
+關(guān)注
關(guān)注
68文章
19899瀏覽量
235438 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4381瀏覽量
64947 -
命令
+關(guān)注
關(guān)注
5文章
738瀏覽量
22898 -
C++
+關(guān)注
關(guān)注
22文章
2119瀏覽量
75363 -
編譯器
+關(guān)注
關(guān)注
1文章
1662瀏覽量
50247
發(fā)布評(píng)論請(qǐng)先 登錄
C語言常用的預(yù)處理命令
c語言預(yù)處理命令以什么開頭
C語言預(yù)處理命令有哪些?
Tcl/Tk命令與C/C++的集成研究
基于51單片機(jī)--C語言之預(yù)處理總結(jié)
C語言常用的預(yù)處理命令和循環(huán)左移右移函數(shù)的詳細(xì)資料概述
C語言程序設(shè)計(jì)教程之預(yù)處理命令的詳細(xì)資料說明

C++的const多文件編譯預(yù)處理的資料說明

8051單片機(jī)的預(yù)處理命令的詳細(xì)資料說明

C51的預(yù)處理命令和用戶配置文件詳細(xì)資料說明

C語言預(yù)處理命令的分類和工作原理詳細(xì)說明

C語言預(yù)處理指令及分類
C語言預(yù)處理命令是什么

C預(yù)處理器及其工作原理

評(píng)論