資料介紹
在將一個C源程序轉換為可執行程序的過程中, 編譯預處理是最初的步驟. 這一步驟是由預處理器(preprocessor)來完成的. 在源流程序被編譯器處理之前, 預處理器首先對源程序中的"宏(macro)"進行處理.
C初學者可能對預處理器沒什么概念, 這是情有可原的: 一般的C編譯器都將預處理, 匯編, 編譯, 連接過程集成到一起了. 編譯預處理往往在后臺運行. 在有的C編譯器中, 這些過程統統由一個單獨的程序來完成, 編譯的不同階段實現這些不同的功能. 可以指定相應的命令選項來執行這些功能. 有的C編譯器使用分別的程序來完成這些步驟. 可單獨調用這些程序來完成. 在gcc中, 進行編譯預處理的程序被稱為CPP, 它的可執行文件名為cpp.
編譯預處理命令的語法與C語言的語法是完全獨立的. 比如: 你可以將一個宏擴展為與C語法格格不入的內容, 但該內容與后面的語句結合在一個若能生成合法的C語句, 也是可以正確編譯的.
(一) 預處理命令簡介
--------------------------------------------------------------------------------
預處理命令由#(hash字符)開頭, 它獨占一行, #之前只能是空白符. 以#開頭的語句就是預處理命令, 不以#開頭的語句為C中的代碼行. 常用的預處理命令如下:
#define????????????? 定義一個預處理宏
#undef???????????? 取消宏的定義
#include??????????? 包含文件命令
#include_next?? 與#include相似, 但它有著特殊的用途
#if????????????????????? 編譯預處理中的條件命令, 相當于C語法中的if語句
#ifdef??????????????? 判斷某個宏是否被定義, 若已定義, 執行隨后的語句
#ifndef???????????? 與#ifdef相反, 判斷某個宏是否未被定義
#elif????????????????? 若#if, #ifdef, #ifndef或前面的#elif條件不滿足, 則執行#elif之后的語句, 相當于C語法中的else-if
#else??????????????? 與#if, #ifdef, #ifndef對應, 若這些條件不滿足, 則執行#else之后的語句, 相當于C語法中的else
#endif????????????? #if, #ifdef, #ifndef這些條件命令的結束標志.
defined????????? 與#if, #elif配合使用, 判斷某個宏是否被定義
#line??????????????? 標志該語句所在的行號
#????????????????????? 將宏參數替代為以參數值為內容的字符竄常量
##?????????????????? 將兩個相鄰的標記(token)連接為一個單獨的標記
#pragma?????? 說明編譯器信息
#warning?????? 顯示編譯警告信息
#error????????? 顯示編譯錯誤信息
(二) 預處理的文法
--------------------------------------------------------------------------------
預處理并不分析整個源代碼文件, 它只是將源代碼分割成一些標記(token), 識別語句中哪些是C語句, 哪些是預處理語句. 預處理器能夠識別C標記, 文件名, 空白符, 文件結尾標志.
預處理語句格式:??? #command name(...) token(s)
1, command預處理命令的名稱, 它之前以#開頭, #之后緊隨預處理命令, 標準C允許#兩邊可以有空白符, 但比較老的編譯器可能不允許這樣. 若某行中只包含#(以及空白符), 那么在標準C中該行被理解為空白. 整個預處理語句之后只能有空白符或者注釋, 不能有其它內容.
2, name代表宏名稱, 它可帶參數. 參數可以是可變參數列表(C99).
3, 語句中可以利用""來換行.
e.g.
# define ONE 1 /* ONE == 1 */
等價于: #define ONE 1
#define err(flag, msg) if(flag) \
?? printf(msg)
等價于: #define err(flag, msg) if(flag) printf(msg)
(三) 預處理命令詳述
--------------------------------------------------------------------------------
1, #define
#define命令定義一個宏:
#define MACRO_NAME(args) tokens(opt)
之后出現的MACRO_NAME將被替代為所定義的標記(tokens). 宏可帶參數, 而后面的標記也是可選的.
對象宏
不帶參數的宏被稱為"對象宏(objectlike macro)"
#define經常用來定義常量, 此時的宏名稱一般為大寫的字符串. 這樣利于修改這些常量.
e.g.
#define MAX 100
int a[MAX];
#ifndef __FILE_H__
#define __FILE_H__
#include "file.h"
#endif
#define __FILE_H__ 中的宏就不帶任何參數, 也不擴展為任何標記. 這經常用于包含頭文件.
要調用該宏, 只需在代碼中指定宏名稱, 該宏將被替代為它被定義的內容.
函數宏
帶參數的宏也被稱為"函數宏". 利用宏可以提高代碼的運行效率: 子程序的調用需要壓棧出棧, 這一過程如果過于頻繁會耗費掉大量的CPU運算資源. 所以一些代碼量小但運行頻繁的代碼如果采用帶參數宏來實現會提高代碼的運行效率.
函數宏的參數是固定的情況
函數宏的定義采用這樣的方式: #define name( args ) tokens
其中的args和tokens都是可選的. 它和對象宏定義上的區別在于宏名稱之后不帶括號.
注意, name之后的左括號(必須緊跟name, 之間不能有空格, 否則這就定義了一個對象宏, 它將被替換為 以(開始的字符串. 但在調用函數宏時, name與(之間可以有空格.
e.g.
#define mul(x,y) ((x)*(y))
注意, 函數宏之后的參數要用括號括起來, 看看這個例子:
e.g.
#define mul(x,y) x*y
"mul(1, 2+2);" 將被擴展為: 1*2 + 2
同樣, 整個標記串也應該用括號引用起來:
e.g.
#define mul(x,y) (x)*(y)
sizeof mul(1,2.0) 將被擴展為 sizeof 1 * 2.0
調用函數宏時候, 傳遞給它的參數可以是函數的返回值, 也可以是任何有意義的語句:
e.g.
mul (f(a,b), g(c,d));
e.g.
#define insert(stmt) stmt
insert ( a="1"; b="2";) 相當于在代碼中加入 a="1"; b="2" .
insert ( a="1", b="2";) 就有問題了: 預處理器會提示出錯: 函數宏的參數個數不匹配. 預處理器把","視為參數間的分隔符.
insert ((a=1, b="2";)) 可解決上述問題.
在定義和調用函數宏時候, 要注意一些問題:
1, 我們經常用{}來引用函數宏被定義的內容, 這就要注意調用這個函數宏時的";"問題.
example_3.7:
#define swap(x,y) { unsigned long _temp=x; x="y"; y=_tmp}
如果這樣調用它: "swap(1,2);" 將被擴展為: { unsigned long _temp=1; 1=2; 2=_tmp};
明顯后面的;是多余的, 我們應該這樣調用: swap(1,2)
雖然這樣的調用是正確的, 但它和C語法相悖, 可采用下面的方法來處理被{}括起來的內容:
#define swap(x,y) \
?? do { unsigned long _temp=x; x="y"; y=_tmp} while (0)
swap(1,2); 將被替換為:
do { unsigned long _temp=1; 1=2; 2=_tmp} while (0);
在Linux內核源代碼中對這種do-while(0)語句有這廣泛的應用.
2, 有的函數宏是無法用do-while(0)來實現的, 所以在調用時不能帶上";", 最好在調用后添加注釋說明.
eg_3.8:
#define incr(v, low, high) \
?? for ((v) = (low),; (v) <= (high); (v)++)
只能以這樣的形式被調用: incr(a, 1, 10) /* increase a form 1 to 10 */
函數宏中的參數包括可變參數列表的情況
C99標準中新增了可變參數列表的內容. 不光是函數, 函數宏中也可以使用可變參數列表.
#define name(args, ...) tokens
#define name(...) tokens
"..."代表可變參數列表, 如果它不是僅有的參數, 那么它只能出現在參數列表的最后. 調用這樣的函數宏時, 傳遞給它的參數個數要不少于參數列表中參數的個數(多余的參數被丟棄).
通過__VA_ARGS__來替換函數宏中的可變參數列表. 注意__VA_ARGS__只能用于函數宏中參數中包含有"..."的情況.
e.g.
#ifdef DEBUG
#define my_printf(...) fprintf(stderr, __VA_ARGS__)
#else
#define my_printf(...) printf(__VA_ARGS__)
#endif
tokens中的__VA_ARGS__被替換為函數宏定義中的"..."可變參數列表.
注意在使用#define時候的一些常見錯誤:
#define MAX = 100
#define MAX 100;
=, ; 的使用要值得注意. 再就是調用函數宏是要注意, 不要多給出";".
注意: 函數宏對參數類型是不敏感的, 你不必考慮將何種數據類型傳遞給宏. 那么, 如何構建對參數類型敏感的宏呢? 參考本章的第九部分, 關于"##"的介紹.
?
關于定義宏的另外一些問題
(1) 宏可以被多次定義, 前提是這些定義必須是相同的. 這里的"相同"要求先后定義中空白符出現的位置相同, 但具體的空白符類型或數量可不同, 比如原先的空格可替換為多個其他類型的空白符: 可為tab, 注釋...
e.g.
#define NULL 0
#define NULL /* null pointer */???? 0
上面的重定義是相同的, 但下面的重定義不同:
#define fun(x) x+1
#define fun(x) x + 1 或: #define fun(y) y+1
如果多次定義時, 再次定義的宏內容是不同的, gcc會給出"NAME redefined"警告信息.
應該避免重新定義函數宏, 不管是在預處理命令中還是C語句中, 最好對某個對象只有單一的定義. 在gcc中, 若宏出現了重定義, gcc會給出警告.
(2) 在gcc中, 可在命令行中指定對象宏的定義:
e.g.
$ gcc -Wall -DMAX=100 -o tmp tmp.c
相當于在tmp.c中添加" #define MAX 100".
那么, 如果原先tmp.c中含有MAX宏的定義, 那么再在gcc調用命令中使用-DMAX, 會出現什么情況呢?
---若-DMAX=1, 則正確編譯.
---若-DMAX的值被指定為不為1的值, 那么gcc會給出MAX宏被重定義的警告, MAX的值仍為1.
注意: 若在調用gcc的命令行中不顯示地給出對象宏的值, 那么gcc賦予該宏默認值(1), 如: -DVAL == -DVAL=1
(3) #define所定義的宏的作用域
宏在定義之后才生效, 若宏定義被#undef取消, 則#undef之后該宏無效. 并且字符串中的宏不會被識別。
- C語言宏定義與預處理、函數和函數庫
- C語言預處理命令的分類和工作原理詳細說明
- C51的預處理命令和用戶配置文件詳細資料說明 6次下載
- 8051單片機的預處理命令的詳細資料說明
- C++的const多文件編譯預處理的資料說明 0次下載
- C語言程序設計教程之預處理命令的詳細資料說明 13次下載
- C語言程序設計教程之編譯預處理的詳細資料說明 2次下載
- C語言程序設計教程之如何進行函數與編譯預處理資料概述 4次下載
- C程序設計教程之如何進行編譯預處理 5次下載
- C語言常用的預處理命令和循環左移右移函數的詳細資料概述 71次下載
- 如何學習c語言?C語言學習筆記資料免費下載 66次下載
- C語言教程之C語言中級培訓教程—預處理電子課件免費下載 0次下載
- TMS320C6000最優編譯器 4次下載
- 第6章 函數與編譯預處理 2次下載
- C語言基礎教材
- C語言生成可執行二進制文件的具體過程 640次閱讀
- 一招掌握C語言代碼如何變成bin文件? 3381次閱讀
- C預處理器及其工作原理 679次閱讀
- C語言有哪些預處理操作? 643次閱讀
- C語言必備知識編譯預處理 1112次閱讀
- C程序的完整編譯過程 3596次閱讀
- 預處理相關知識點總結 636次閱讀
- C語言預處理命令是什么 2741次閱讀
- C語言中條件編譯詳解 3131次閱讀
- 詳解#define的奇妙用法 1821次閱讀
- VScode編譯器如何配置C/C++編譯環境 5662次閱讀
- C語言中#和##符號有什么樣的作用 4.6w次閱讀
- c語言程序設計知識點 9928次閱讀
- c語言實現fifo算法及代碼 1.7w次閱讀
- 基于FPGA處理器的C編譯指令 2703次閱讀
下載排行
本周
- 1電子電路原理第七版PDF電子教材免費下載
- 0.00 MB | 1490次下載 | 免費
- 2單片機典型實例介紹
- 18.19 MB | 92次下載 | 1 積分
- 3S7-200PLC編程實例詳細資料
- 1.17 MB | 27次下載 | 1 積分
- 4筆記本電腦主板的元件識別和講解說明
- 4.28 MB | 18次下載 | 4 積分
- 5開關電源原理及各功能電路詳解
- 0.38 MB | 10次下載 | 免費
- 6基于AT89C2051/4051單片機編程器的實驗
- 0.11 MB | 4次下載 | 免費
- 7藍牙設備在嵌入式領域的廣泛應用
- 0.63 MB | 3次下載 | 免費
- 89天練會電子電路識圖
- 5.91 MB | 3次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234313次下載 | 免費
- 2PADS 9.0 2009最新版 -下載
- 0.00 MB | 66304次下載 | 免費
- 3protel99下載protel99軟件下載(中文版)
- 0.00 MB | 51209次下載 | 免費
- 4LabView 8.0 專業版下載 (3CD完整版)
- 0.00 MB | 51043次下載 | 免費
- 5555集成電路應用800例(新編版)
- 0.00 MB | 33562次下載 | 免費
- 6接口電路圖大全
- 未知 | 30320次下載 | 免費
- 7Multisim 10下載Multisim 10 中文版
- 0.00 MB | 28588次下載 | 免費
- 8開關電源設計實例指南
- 未知 | 21539次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935053次下載 | 免費
- 2protel99se軟件下載(可英文版轉中文版)
- 78.1 MB | 537791次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420026次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234313次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233045次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191183次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183277次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138039次下載 | 免費
評論
查看更多