0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C/C++預(yù)處理命令的工作原理及分類

CHANBAEK ? 來源:明解嵌入式 ? 作者:Sharemaker001 ? 2023-04-15 11:32 ? 次閱讀

摘要:在C/C++語言編程過程中,經(jīng)常會用到如#include、#define等指令,同時也會涉及到大量的預(yù)處理與條件編譯,這樣做的好處可以使代碼更利于移植移植性,也讓代碼易于修改。 因此引入了預(yù)處理與條件編譯的概念。

預(yù)處理的行為是由指令控制的。 所有的預(yù)處理器命令都是以#開頭,它必須是第一個非空字符。 預(yù)處理指令由預(yù)處理程序(預(yù)處理器)操作。

預(yù)處理器不是編譯器的組成部分,但是它是編譯過程中一個單獨的步驟。 因此, 預(yù)處理器只不過是一個文本替換工具而已,它們會指示編譯器在實際編譯之前完成所需的預(yù)處理。 通俗來講預(yù)處理命令的作用就是在編譯和鏈接之前,對源文件進行一些文本方面的操作,比如文本替換、文件包含、刪除部分代碼等,這個過程叫做預(yù)處理(在編譯之前對源文件進行簡單加工)

相比其他編程語言,C/C++語言更依賴預(yù)處理器,故在閱讀或開發(fā)C/C++程序過程中,可能會接觸大量的預(yù)處理指令。 預(yù)處理指令不屬于C/C++語言的語法,但在一定意義上可以說預(yù)處理擴展了C/C++。 預(yù)處理命令的分類主要劃分為以下幾種類型:

1、宏定義

#define命令并不是真正的定義符號常量,而是定義一個可以替換的宏。 被定義為宏的標示符稱為“宏名”。 在編譯預(yù)處理過程時,對程序中所有出現(xiàn)的“宏名”,都用宏定義中的字符串去代換,這稱為“宏代換”或“宏展開”。 宏替換在編譯前進行,不分配內(nèi)存; 宏展開不占運行時間,只占編譯時間; 宏替換只作替換,不做計算。

#define NEMBER 9 //#define 宏名 文本
#define M(a, b) a*b //#define 宏名(參數(shù)表) 文本
#define SWITCHON  //#define 宏名  //(定義一個條件編譯的開關(guān)字段)
#define NAME(n) num ## 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
//在宏定義中,形參列表的最后一個參數(shù)為省略號“…”,而“__VA_ARGS__”就可以被用在替換文本中,來表示省略號“…”代表了什么。
//而上面例子宏代換之后為:printf(“hello\\n”);

#undef指令刪除前面定義的宏名字(也就是#define的標識符)。 也就是說,它“不定義”宏。 (注意:如果標識符當前沒有被定義成一個宏名稱,那么就會忽略該指令),一般形式為:

#undef  NEMBER      //取消之前已定義的NEMBER
#define NEMBER 100  //重新定義NUMBE為100

2、系統(tǒng)預(yù)定義的宏

LINE : 當前源文件的行號,整數(shù)

FILE : 當前源文件名,char 字符串,文件的完整路徑和文件名**

DATE : 當前編譯日期,char 字符串,格式:月 日 年

TIME : 當前編譯時間,char 字符串,格式:時 分 秒

STDC : 整數(shù) 1,表示兼容 ANSI/ISO C 標準,配合 #if 使用

**TIMESTAMP ** : 最后一次修改當前文件的時間戳,char 字符串,格式:年 月份 日期 時 分 秒

3、文件包含

當一個C語言程序由多個文件模塊組成時,主模塊中一般包含main函數(shù)和一些當前程序?qū)S玫暮瘮?shù)。 程序從main函數(shù)開始執(zhí)行,在執(zhí)行過程中,可調(diào)用當前文件中的函數(shù),也可調(diào)用其他文件模塊中的函數(shù)。

如果在模塊中要調(diào)用其他文件模塊中的函數(shù),首先必須在主模塊中聲明該函數(shù)原型。 一般都是采用文件包含的方法,包含其他文件模塊的頭文件。

包含文件的格式有#include后面跟尖括號<>和雙引號“”之分。 兩者的主要差別是搜索路徑的不同。 C的標準庫加.h,C++標準庫可以不加.h。

尖括號形式:如#include

雙引號形式:如#include“para.h”,首先到當前工作目錄下查找該文件,如果未發(fā)現(xiàn),再按尖括號包含時的辦法到系統(tǒng)目錄下查找。 包含自定義的頭文件,一般采用該方式。 雖然系統(tǒng)標準庫頭文件采用此方式也正確,但浪費了不必要的搜索時間,故系統(tǒng)標準庫頭文件不建議采用該包含方式。

4、條件編譯

條件編譯允許程序員有選擇按照不同的條件去編譯程序的不同部分,從而得到不同的目標代碼。 使用條件編譯,可方便地處理程序的調(diào)試版本和正式版本,也可使用條件編譯使程序的移植更方便。

常見的條件編譯指令有 #if、#elif、#else、#endif、#ifdef、#ifndef。

#if、#elif、#else、#endif的使用和if、elseif 、else的使用非常相似,一般使用格式如下:

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

執(zhí)行起來就是,如果整形常量表達式為真,則執(zhí)行程序段1,否則繼續(xù)往后判斷依次類推(注意是整形常量表達式),最后#endif是#if的結(jié)束標志。

#ifdef的作用是判斷某個宏是否定義,如果該宏已經(jīng)定義則執(zhí)行后面的代碼,#ifndef恰好和#ifdef相反,一般使用格式如下:

//ifdef
#ifdef  宏名
    程序段1
#else
    程序段2
#endif
//ifndef
#ifndef 宏名
    程序段1 
#else 
    程序段2 
#endif

#ifdef表示如果該宏已被定義過,則對“程序段1”進行編譯,否則對“程序段2”進行編譯(這個和上面的#if一樣最后都需要#endif),上述格式也可以不用#else,這一點上和if else相同。

#ifndef表示如果該宏未被定義,則對“程序段1”進行編譯,否則對“程序段2”進行編譯。

5、特殊命令

#line 可以改變 LINE 和 _FILE_兩個宏的內(nèi)容,即為其指定新的值。 其本質(zhì)是重定義 LINEFILE ,主要有以下兩種形式:

#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 :當預(yù)處理器預(yù)處理到#error命令時將停止編譯并輸出用戶自定義的錯誤消息,一般用于調(diào)試程序。

#error [用戶自定義的錯誤消息]
//注:上述語法成份中的方括號"[]"代表用戶自定義的錯誤消息可以省略不寫。
//舉例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 指令,但不會導(dǎo)致取消預(yù)處理,程序繼續(xù)編譯,不會影響程序的正常運行。 #warning 指令之后的信息在預(yù)處理繼續(xù)之前作為消息輸出,產(chǎn)生警告。

#warning [用戶自定義的警告信息]
#warning Sorry,an warning has occurred!

#pragma:是功能比較豐富且靈活的指令,可以有不同的參數(shù)選擇,從而完成相應(yīng)的特 定功能操作。 #pragma指令是計算機或操作系統(tǒng)特定的,并且通常對于每個編譯器而言都有所不同。 #pragma指令可用于條件語句以提供新的預(yù)處理器功能,或為編譯器提供實現(xiàn)所定義的信息

其格式一般為: #pragma Para。 其中Para 為參數(shù),參數(shù)可以有 message 類型、code_seg、once、warning、pack 等,具體可以在網(wǎng)上詳細查看。 舉兩個常用的例子:

#pragma一次

只要在頭文件的最開始加入這條指令就能夠保證指定該文件在編譯源代碼文件時僅由編譯器包含(打開)一次,使用 #pragma once 可減少生成次數(shù),和使用預(yù)處理宏定義來避免多次包含文件的內(nèi)容的效果是一樣的,但是需要鍵入的代碼少,可減少錯誤率,這條指令實際上在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),就是說這個編譯系統(tǒng)上能用,但在其他編譯系統(tǒng)不一定可以,也就是說移植性差,不過基本上已經(jīng)是每個編譯器都有這個定義了。

#pragma包 (n)

指定結(jié)構(gòu)、聯(lián)合和類成員的封裝對齊。 其實就是改變編譯器的內(nèi)存對齊方式。 這個功能對于集合數(shù)據(jù)體使用,默認的數(shù)據(jù)的對齊方式占用內(nèi)存比較大,可進行修改。 在沒有參數(shù)的情況下調(diào)用pack會將n設(shè)置為編譯器選項中設(shè)置的值。 如果未設(shè)置編譯器選項,windows默認為8,linux默認為4。 具體的使用方法為,其中n稱為對齊系數(shù),取值必須是2的冪次方,即1、2、4、8、16等。

1. #pragma pack(show)     以警告信息的形式顯示當前字節(jié)對齊的值.
2. #pragma pack(n)        將當前字節(jié)對齊值設(shè)為 n .
3. #pragma pack()         將當前字節(jié)對齊值設(shè)為默認值(通常是8) .
4. #pragma pack(push)     將當前字節(jié)對齊值壓入編譯棧棧頂.
5. #pragma pack(pop)      將編譯棧棧頂?shù)淖止?jié)對齊值彈出并設(shè)為當前值.
6. #pragma pack(push, n)  先將當前字節(jié)對齊值壓入編譯棧棧頂, 然后再將 n 設(shè)為當前值.
7. #pragma pack(pop, n)   將編譯棧棧頂?shù)淖止?jié)對齊值彈出, 然后丟棄, 再將 n 設(shè)為當前值.
8. #pragma pack(push, identifier)        將當前字節(jié)對齊值壓入編譯棧棧頂, 然后將棧中保存該值的位置標識為 identifier .
10. #pragma pack(pop, identifier)        將編譯棧棧中標識為 identifier 位置的值彈出, 并將其設(shè)為當前值. 注意, 如果棧中所標識的位置之上還有值, 那會先被彈出并丟棄.
11. #pragma pack(push, identifier, n)    將當前字節(jié)對齊值壓入編譯棧棧頂, 然后將棧中保存該值的位置標識為 identifier, 再將 n 設(shè)為當前值.
12. #pragma pack(pop, identifier, n)     將編譯棧棧中標識為 identifier 位置的值彈出, 然后丟棄, 再將 n 設(shè)為當前值. 注意, 如果棧中所標識的位置之上還有值, 那會先被彈出并丟棄.
//注意: 如果在棧中沒有找到 pop 中的標識符, 則編譯器忽略該指令, 而且不會彈出任何值.

通常成對使用:

#pragma pack (n)          //作用:編譯器將按照n個字節(jié)對齊。
#pragma pack ()           //作用:取消自定義字節(jié)對齊方式。


#pragma  pack (push,1)    //作用:是指把原來對齊方式設(shè)置壓棧,并設(shè)新的對齊方式設(shè)置為一個字節(jié)對齊
#pragma pack(pop)         //作用:恢復(fù)對齊狀態(tài)
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19286

    瀏覽量

    229865
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4331

    瀏覽量

    62629
  • 命令
    +關(guān)注

    關(guān)注

    5

    文章

    684

    瀏覽量

    22027
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2108

    瀏覽量

    73657
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1634

    瀏覽量

    49134
收藏 人收藏

    評論

    相關(guān)推薦

    C語言常用的預(yù)處理命令

    1.基礎(chǔ)知識(1)C語言常用的預(yù)處理命令——typedef具體可見C語言基礎(chǔ)語法知識。(2)延時函數(shù)(知識粗略估計延時時間,若要精確延時,需要用到后續(xù)的定時器)void delay(u
    發(fā)表于 07-14 07:10

    c語言預(yù)處理命令以什么開頭

    c語言預(yù)處理命令以什么開頭,目前我并沒有windows軟件編寫經(jīng)驗,對C語言的應(yīng)用也僅限于各種單片機的編程,所以對預(yù)處理的理解也只限于單片機
    發(fā)表于 07-20 07:00

    C語言預(yù)處理命令有哪些?

    不止。先看幾個個常識性問題: A) 預(yù)處理C 語言的一部分嗎? B) 包含“#”號的都是預(yù)處理嗎? C) 預(yù)處理指令后面都不需要加“;”號
    發(fā)表于 06-25 06:15

    Tcl/Tk命令C/C++的集成研究

    針對 Tcl/Tk 腳本中需要調(diào)用C/C++函數(shù)的問題,簡要說明了Tcl/Tk 命令的運行機理,給出了一個使用Tcl/Tk 命令來調(diào)用C/
    發(fā)表于 08-26 09:47 ?36次下載

    預(yù)處理器的工作原理作用

    預(yù)處理器的工作原理作用,希望對學(xué)者們有幫助。
    發(fā)表于 10-29 11:40 ?0次下載

    基于51單片機--C語言之預(yù)處理總結(jié)

    編譯預(yù)處理器是C語言編譯器的一個重要組成部分。很好的利用C語言的預(yù)處理命令可以增強代碼的可讀性,靈活性,和易于修改等特點,便于程序的結(jié)構(gòu)化。
    發(fā)表于 08-17 10:48 ?1087次閱讀

    C語言常用的預(yù)處理命令和循環(huán)左移右移函數(shù)的詳細資料概述

    本文檔的主要內(nèi)容詳細介紹的是C語言常用的預(yù)處理命令和循環(huán)左移右移函數(shù)的詳細資料概述。
    發(fā)表于 10-22 08:00 ?71次下載

    C語言程序設(shè)計教程之預(yù)處理命令的詳細資料說明

    預(yù)處理命令的主要內(nèi)容:三種預(yù)處理命令,宏定義,文件包含,條件編譯
    發(fā)表于 02-26 14:43 ?13次下載
    <b class='flag-5'>C</b>語言程序設(shè)計教程之<b class='flag-5'>預(yù)處理</b><b class='flag-5'>命令</b>的詳細資料說明

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

    本文檔的主要內(nèi)容詳細介紹的是C++的const多文件編譯預(yù)處理的資料說明包括了:1、const型常量,2、常對象,3、常成員函數(shù),4、常數(shù)據(jù)成員,5、常引用,6、多文件,7、編譯預(yù)處,8、多文件結(jié)構(gòu)中使用編譯預(yù)處理的問題
    發(fā)表于 04-03 08:00 ?0次下載
    <b class='flag-5'>C++</b>的const多文件編譯<b class='flag-5'>預(yù)處理</b>的資料說明

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

    在編寫程序時,經(jīng)常會使用以“#”開頭的預(yù)處理命令。在對程序進行編譯時,會有專門的預(yù)處理程序來對這些命令進行處理
    發(fā)表于 06-11 17:47 ?0次下載
    8051單片機的<b class='flag-5'>預(yù)處理</b><b class='flag-5'>命令</b>的詳細資料說明

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

    用戶編寫的C51程序代碼只能控制程序的執(zhí)行流程,若要對編譯程序進行操作,就要用到預(yù)處理命令。在編譯環(huán)境對源程序進行編譯前,先對程序中的預(yù)處理命令
    發(fā)表于 03-19 14:52 ?6次下載
    <b class='flag-5'>C</b>51的<b class='flag-5'>預(yù)處理</b><b class='flag-5'>命令</b>和用戶配置文件詳細資料說明

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

    器,故在閱讀或開發(fā) C/C++ 程序過程中,可能會接觸大量的預(yù)處理指令。 1、預(yù)處理指令及分類 C
    發(fā)表于 11-25 10:34 ?18次下載
    <b class='flag-5'>C</b>語言<b class='flag-5'>預(yù)處理</b><b class='flag-5'>命令</b>的<b class='flag-5'>分類</b>和<b class='flag-5'>工作原理</b>詳細說明

    C語言預(yù)處理指令及分類

    C/C++ 程序中的源代碼中包含以 # 開頭的各種編譯指令,這些指令稱為預(yù)處理指令。預(yù)處理指令不屬于 C/
    的頭像 發(fā)表于 11-29 10:14 ?2268次閱讀

    C語言預(yù)處理命令是什么

    我們在寫C語言程序時經(jīng)常使用庫函數(shù)之前,應(yīng)該用`#include`引入對應(yīng)的頭文件。這種以`#`號開頭的命令稱為預(yù)處理命令。
    的頭像 發(fā)表于 02-17 13:59 ?2698次閱讀
    <b class='flag-5'>C</b>語言<b class='flag-5'>預(yù)處理</b><b class='flag-5'>命令</b>是什么

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

    C預(yù)處理器(C Pre-Processor)也常簡寫為 CPP,是一個與 C 編譯器獨立的小程序,預(yù)編譯器并不理解 C 語言語法,它僅是在程
    的頭像 發(fā)表于 03-12 14:14 ?641次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>預(yù)處理</b>器及其<b class='flag-5'>工作原理</b>