1.宏定義和函數(shù)的區(qū)別
-------------------------------------------------------------------------------------------------------------------
宏:宏定義是C提供的三種預處理功能的其中一種,這三種預處理包括:
(1)宏定義
(2)文件包含
(3)條件編譯
1.不帶參數(shù)的宏定義:
格式:#define 標識符 字符串
標示符就是可以替換字符串的宏名稱,編譯器在進行預處理過程中對使用宏替換的地方展開,用“字符串”替換宏名稱,這樣做的好處就是可以對數(shù)字或者隨機數(shù)值指定一個有代表意義的名稱,提高程序的可讀性和通用性,在后期維護中可以方便的修改宏定義中字符串的數(shù)值大小或者其他數(shù)值類型,從而可以控制整個代碼中所有引用宏名的地方。
從占用資源的角度看,編譯器對宏替換不會占用或者分配內存,和函數(shù)比較,調用子函數(shù)需要分配內存,增加系統(tǒng)開銷,但子函數(shù)可以把程序結構模塊化,提高程序的可讀性和聚合度,對比之下,宏也可以有參數(shù),如果在程序中為了不調用子函數(shù)而減小開銷,那么所有過程都寫在一個函數(shù)中,并且沒有自注釋的名稱,程序的可讀性就會降低,畢竟代碼是處理現(xiàn)實世界中事務數(shù)據(jù)關系的一種抽象,但不是一個人的,應該是像一首簡潔,優(yōu)美的詩,描述了美好的事務,所以折中之下,宏替換是個不錯的選擇。
雖然宏替換占用了編譯器的時間,所謂“有得必有失”,減小了程序執(zhí)行的資源消耗,這未嘗不是一種平衡。
宏的一些特點(引用):
(1)宏名一般用大寫
(2)使用宏可提高程序的通用性和易讀性,減少不一致性,減少輸入錯誤和便于修改。例如:數(shù)組大小常用宏定義 ,可以理解數(shù)組大小代表具體含義,便于二次維護。
(3)預處理是在編譯之前的處理,而編譯工作的任務之一就是語法檢查,預處理不做語法檢查。
(4)宏定義末尾不加分號;
(5)宏定義寫在函數(shù)的花括號外邊,作用域為其后的程序,通常在文件的最開頭。 (6)可以用#undef命令終止宏定義的作用域
(7)宏定義可以嵌套
(8)字符串" "中永遠不包含宏
(9)宏定義不分配內存,變量定義分配內存。
帶參數(shù)的宏:
格式: #define S(r) r*r ////////
(1)實參如果是表達式容易出問題
area=S(a+b);第一步換為area=r*r;,第二步被換為area=a+b*a+b;
正確的宏定義是#define S(r) ((r)*(r)) 。
(2)宏名和參數(shù)的括號間不能有空格
(3)宏替換只作替換,不做計算,不做表達式求解
(4)函數(shù)調用在編譯后程序運行時進行,并且分配內存。宏替換在編譯前進行,不分配內存
(5)宏的啞實結合不存在類型,也沒有類型轉換。
(6)函數(shù)只有一個返回值,利用宏則可以設法得到多個值
(7)宏展開使源程序變長,函數(shù)調用不會
(8)宏展開不占運行時間,只占編譯時間,函數(shù)調用占運行時間(分配內存、保留現(xiàn)場、值傳遞、返回值。
(9):#define Uint unsigned int 即用 Uint 代替unsigned int 減小了書寫的長度,還有就是不同操作系統(tǒng)的通用變量識別可以統(tǒng)一一致。
引用:宏定義其他冷門、重點知識(實際程序設計中也會用到)
(1) #define NAME "zhangyuncong"
程序中有"NAME"則,它會不會被替換呢?
(2) #define 0x abcd
可以嗎?也就是說,可不可以用把標識符的字母替換成別的東西?
(3) #define NAME "zhang
這個可以嗎?
?。?) #define NAME "zhangyuncong"
程序中有上面的宏定義,并且,程序里有句:
NAMELIST這樣,會不會被替換成"zhangyuncong"LIST
四個題答案都是否定的。
===================================================================
第一個,""內的東西不會被宏替換。這一點應該大都知道。
第二個,宏定義前面的那個必須是合法的用戶標識符
第三個,宏定義也不是說后面東西隨便寫,不能把字符串的兩個""拆開。
第四個:只替換標識符,不替換別的東西。NAMELIST整體是個標識符,而沒有NAME標識符,所以不替換。
也就是說,這種情況下記住:#define 第一位置第二位置
?。?) 不替換程序中字符串里的東西。
?。?) 第一位置只能是合法的標識符(可以是關鍵字)
?。?) 第二位置如果有字符串,必須把""配對。
?。?) 只替換與第一位置完全相同的標識符 。
比如#define MAX(a,b) ((a)>(b)?(a):(b))
則遇到MAX(1+2,value)則會把它替換成:
((1+2)>(value)?(1+2):(value))
注意事項和無參宏差不多。
但還是應注意
#define FUN(a) "a"
則,輸入FUN(345)會被替換成什么?
其實,如果這么寫,無論宏的實參是什么,都不會影響其被替換成"a"的命運。
也就是說,""內的字符不被當成形參,即使它和一模一樣。
那么,你會問了,我要是想讓這里輸入FUN(345)它就替換成"345"該怎么實現(xiàn)呢?
請看下面關于#的用法
3、 有參宏定義中#的用法
#define STR(str) #str
#用于把宏定義中的參數(shù)兩端加上字符串的""
比如,這里STR(my#name)會被替換成"my#name"
一般由任意字符都可以做形參,但以下情況會出錯:
STR())這樣,編譯器不會把“)”當成STR()的參數(shù)。
STR(,)同上,編譯器不會把“,”當成STR的參數(shù)。
STR(A,B)如果實參過多,則編譯器會把多余的參數(shù)舍去。(VC++2008為例)
STR((A,B))會被解讀為實參為:(A,B),而不是被解讀為兩個實參,第一個是(A第二個是B)。
4、 有參宏定義中##的用法
#define WIDE(str) L##str
則會將形參str的前面加上L
比如:WIDE("abc")就會被替換成L"abc"
如果有#define FUN(a,b) vo##a##b()
那么FUN(id ma,in)會被替換成void main()
5、 多行宏定義:
#define doit(m,n) for(int i=0;i<(n);++i)\
{\
m+=i;\
}
。
其中有參宏定義中 #define WIDE("abc") L##str的用法,在高煥堂著作中《UML+OOPC 嵌入式開發(fā)精講》中實現(xiàn)輕量級面向對象的方法中就使用了 ##的用法和多行宏定義用法。
所以根據(jù)上述特點使用好宏定義,可以寫出優(yōu)美的代碼,防止程序中的不一致錯誤,提高可移植性,可讀性,方便修改維護。只要所有引用宏定義的地方都可以做到一次修改就完成。
另外在很多源代碼中有些宏就是函數(shù)名稱。直接重新定義了函數(shù)的名稱,增加了代碼的可閱讀程度。
條件編譯:最開始可能也是出于提高程序設計的方便,對于源程序中某些內容只在特定條件下進行編譯,于是乎條件編譯就出現(xiàn)了,引用百科“一般情況下,源程序中所有的行都參加編譯。但有時希望對其中一部分內容只在滿足一定條件下才進行編譯,即對一部分內容指定編譯條件,這就是“條件編譯”(conditional compile)。條件編譯語句排版時,需考慮以下三種位置:1)條件編譯語句塊與函數(shù)定義體之間不存在相互嵌套(主要在(.h)文件中);2)條件編譯語句塊嵌套在函數(shù)體之外(主要在(.c)文件中);3)條件編譯語句嵌套在函數(shù)體內 (主要在(.c)文件中)。條件編譯指令將決定那些代碼被編譯,而哪些是不被編譯的??筛鶕?jù)表達式的值或某個特定宏是否被定義來確定編譯條件?!?/span>
控制開發(fā)可以是某個宏名是否被定義或者表達式的值來確定是否編譯某部分代碼。
引用:
文件包含命令的一般格式是:#include "文件名"或 #include <文件名>
作用:預處理時,把“文件名”指定的文件內容復制到本文件,再對合并后的文件進行編譯。
用途:避免重復勞動
說明:
1.雙引號和尖引號的區(qū)別:雙引號表示系統(tǒng)現(xiàn)在當前目錄中尋找file所在的目錄,若找不到,再按系統(tǒng)指定的
系統(tǒng)指定的標準方式尋找其它目錄;尖引號僅查找按系統(tǒng)標準方式指定的目錄.
2.一個#define只能指定一個包含文件,如 果要把多個文件都嵌入到源文件之中,則必須使用多個#define。
3.C語言允許嵌套使用#define。
在file1.c文件中,有文件包含命令#include "file2.c",預處理時,先把file2.c的內容復制到文件
file1.c,再對file1.c進行編譯。
從理論上說,#include命令可以包含任何類型的文件,只要這些文件的內容被擴展后符合C語言語法。
2、被包含文件與其所在文件,在預處理后,成為一個文件,因此,如果被包含文件定義有全局變量,在其
它文件中不必用extern關鍵字聲明。但一般不在被包含文件中定義變量。
聯(lián)系客服