第八章巨集 資訊科技系 林偉川 本章簡介 所謂巨集, 就是用 #define 的語法定義一個簡短巨集名稱來取代一長串的算式 根據前處理指令 #define 會由前置處理器處理, 也就是說在前置處理過程中, 巨集名稱都會被代換成指定的算式 2 1
使用巨集的方法 巨集是一種方便的程式設計方式, 在程式中看到簡短的名稱, 就知道是代表那段長的敘述 定義巨集 定義巨集的位置 巨集取代算式 3 定義巨集 使用巨集名稱前, 要先定義 定義巨集的寫法如下 : #define 巨集名稱算式 4 2
定義巨集 #define: 在此是用來定義巨集名稱 巨集名稱 : 巨集名稱與變數名稱的命名規則相同, 但是習慣上會使用大寫字母以免和程式中變數名稱相混 算式 : 討論過的任何算式都可以定義在此 5 定義巨集時注意事項 一個巨集名稱只能取代一個算式 每定義一個巨集就需使用一個 #define 巨集指令的每一項均需以空白分開, 結束時不加分號 : #define INTEREST principal*rate*period #define ENERGY mass*lightspeed*lightspeed 6 3
使用巨集的方法 前置處理器會將程式中出現的巨集名稱, 都換成我們指定的算式 因此對一些內容稍複雜而且又會重複用到的算式, 即可用 #define 指令定義成巨集 寫程式時只需寫巨集名稱即可, 不但節省一些撰寫程式的時間, 閱讀程式碼時也較能一目瞭然 7 定義巨集的位置 只是定義巨集的位置一定要在使用巨集名稱之前 如以下範例 : 8 4
定義巨集的位置 為了避免發生此類的編譯錯誤, 通常我們都會將巨集名稱定義在 #include 與 main ( ) 之間 如以下範例 : #include <stdio.h> #defien MACRO int main(void) { result=macro; } void func( ) { result2=macro; } 9 巨集取代算式 當我們以巨集來取代算式時, 要注意到算式中如果含有變數時, 需要在程式中宣告後, 才可以使用定義算式的巨集名稱 例如 : 定義 1 個求未知數 x 立方值的算式 由於巨集只是定義由前置處理器置換的的內容, 並不具宣告變數的功效, 因此在程式中仍需宣告變數 x 否則當前置處理器將程式碼中的 CUBE 代換成算式 x*x*x 後, 編譯器會不認得 x, 而產生編譯錯誤 語法如下 :#define CUBE x*x*x 10 5
求一數之立方值 11 萬有引力的公式 萬有引力的大小 (FG) 與兩物體的質量 (mass1) 及 (mass2) 乘積成正比, 與彼此距離 (distant) 的平方成反比, 所以萬有引力的公式為 : FG=G*(mass1*mass2)/(distance*distance) G 是萬有引力常數, 所以先用定義常數的方法來定義 G: #define G 6.672e-11 #define FG G*(mass1*mass2)/(distance*distance) 12 6
計算兩物體間的萬有引力 13 計算兩物體間的萬有引力 14 7
執行結果 15 巨集代替簡單的函式 定義帶有引數的巨集 帶有兩個以上引數的巨集名稱 用數值當成巨集的引數值 用算式當成巨集的引數值 16 8
巨集代替簡單的函式 在作運算時, 便可將引數的數值代入巨集名稱所取代的算式中 如此如同呼叫函式時, 可以傳遞引數到函式中作運算 所以, 在函式內容較短的情形下, 我們也可以利用巨集來代替函式 17 定義帶有引數的巨集 要讓巨集有如函式的引數功能, 可在定義的巨集名稱後面加上引數, 寫法如下 : #define 巨集名稱 ( 引數 ) 算式 18 9
引數的使用規則 不需加上資料型別, 使用變數名稱就可以了 可使用兩個以上的引數 使用時, 請用逗號隔開 用來當引數的變數名稱要與算式中的變數名稱相同, 否則編譯的時候會出現無法辨識變數名稱的錯誤 引數的值可以為其他變數 數值或算式 代入算式時, 請一定要加上括號, 以免破壞原算式的運算順序 如以下範例 : #define C(F) (F-32)*5/9 19 溫度度量衡轉換 20 10
溫度度量衡轉換 21 帶有兩個以上引數的巨集名稱 定義的巨集名稱可以帶有兩個以上的引數, 需以逗號將各個引數隔開 並且要注意, 使用引數的數目與變數名稱必須與使用在算式中的相同 將數值當成巨集的引數值, 帶入巨集所定義的算式中, 便可以求出算式的運算結果 假設想要定義一個計算體格指數公式的巨集, 已知體格指數 BMI 等於體重 (kg) 除以身高 (m) 平方 我們可以定義一個帶有體重 weight 與身高 height 兩個引數的巨集 : #defien BMI(w,h) w/(h*h) 22 11
計算體格指數並判斷健康狀態 最理想的 BMI 值是 22;BMI 在 20 以下為體重不足 ;BMI 在 20~25 是理想體重的範圍 ;BMI 在 26~29 間表示微胖 ;BMI 在 30 以上表示過胖 以此資料寫出一個以 BMI 值來判斷體重狀態的程式 : 23 計算體格指數並判斷健康狀態 24 12
計算體格指數並判斷健康狀態 25 計算體格指數並判斷健康狀態 26 13
求多項式的和 27 求多項式的和 28 14
程式執行說明 巨集展開後, 計算過程如下 : 29 當算式代入巨集引數時為甚麼算式要加括號 當我們將算式當成引數, 代入巨集定義的算式中時, 要特別注意到算符的優先權的問題 如果我們以 F (i, i+1) 代入巨集中, 會得到如下的式子 : 30 15
當算式代入巨集引數時為甚麼算式要加括號 同樣算到第 5 項的和, 結果如下 : 由於 先乘除後加減 的關係, 代入後的算式並非原先我們所期望的多項式 31 當算式代入巨集引數時為甚麼算式要加括號 如果怕代入算式時, 可以在定義巨集時, 就將算式加上括號 定義語法如下 : #define F(x,y) (x)*(x)+(y)*(y) 如此一來, 算式代入之後就會變成如下 : F(x,y)=x*x+y*y F(i,(i+1))=i*i+(i+1)*(i+1) 32 16
巨集與函式的比較 巨集與函式執行速度比較 巨集與函式的內容之比較 33 巨集與函式的比較 34 17
巨集與函式的比較 由上表我們可以歸納出一點, 就是為提高程式執行效率, 我們可將巨集用於定義簡單的算式, 如 distant = rate * time, 以 distant 來代替後面的 rate * time 算式, 可增加使用上的方便性 而函式則適用於功能較複雜 且不容易定義成巨集的運算, 如迴圈運算, 條件式判斷等 35 巨集與函式執行速度比較 巨集的執行速度會比函式快, 因為當程式在編譯前, 所有巨集名稱都被會置換成所代表的運算式, 就如同將變數名稱置換成變數值來運算一樣 36 18
巨集與函式執行速度比較 如果程式執行遇到呼叫函式, 程式控制權會轉移到執行函式的內容 呼叫幾次函式, 就轉換幾次程式控制權 如以一來, 便比巨集多了轉換控制權的時間, 所以函式會比較慢 : 37 測試巨集與函式的執行時間 38 19
測試巨集與函式的執行時間 39 測試巨集與函式的執行時間 40 20
巨集與函式的比較 巨集重複使用多次, 其執行速度的優勢, 才會比較明顯 巨集重複使用多次缺點 : 執行檔的檔案大小會比使用函式的程式大 因為使用巨集時, 雖然在寫程式時, 只需寫巨集名稱, 但在編譯前, 它們都會被擴展成巨集程式的內容, 等於讓程式擴張, 所以雖然執行快一些 檔案也會變大一些 41 巨集與函式的比較 以函式的方式來設計, 則呼叫函式的程式碼, 只是在執行時會轉到函式的程式碼來執行, 並不會使程式擴張, 所以雖然在執行時, 有呼叫 傳回的動作使執行時間增加一點點, 但也因此不會讓程式執行檔變大 42 21
巨集與函式的比較 在決定該使用巨集或函式時, 若兩種方法實作的便利性差不多, 可再考量使用的次數 : 若使用次數不多, 則使用巨集或函式都差不多 若要重複使用很多次, 就要確定您在乎的是程式執行的效率 ( 巨集較好 ) 或是希望程式不要太大 ( 函式較佳 ), 然後依您的決定來選擇使用巨集或函式來設計程式 43 巨集與函式的內容之比較 雖然巨集的執行比函式快, 但是因為巨集的定義內容必須在一行內完成, 所以巨集不適合較為複雜的運算, 如以下的迴圈計算從 1 到 n 所有整數和的函式 : 44 22
巨集與函式的內容之比較 物理電學中的電壓公式電壓 (v) = 電流 (I) * 電阻 (R) 定義成巨集, 當然比需要原型宣告, 又要定義內容的函式, 要簡單許多 45 綜合演練 各種巨集定義的結合應用 : 求球體體積 巨集的應用 : 解二元一次聯立方程式 巨集與函式的綜合應用 : 匯率換算 46 23
各種巨集定義的結合應用 : 球體體積 已知球體的體積計算公式為 4/3 *π* r3, 利用巨集定義的方法, 我們可以先把圓週率 π 以及計算公式以 #define 的方式定義好, 再藉由鍵盤輸入半徑計算後, 便可得到球體體積, 程式如下 : 47 求球體體積 48 24
程式執行說明 第 2 行以定義巨集常數的方式定義圓週率 第 3 行定義求立方值的巨集 第 4 行定義球體體積公式的巨集 49 巨集的應用 : 二元一次聯立方程式 在數學中, 二元一次聯立方程式是對兩個具有相同的兩個未知數 (x,y) 的多項式求 x y 的值, 如 a*x + b*y = c d*x + e*y = f 中, a b c d e f 是方程式的係數為已知的常數值, 求未知數 x y 的值 聯立方程式的公式可以寫成 x = (c*e- f*b) / (a*e- d*b) y = (a*f- d*c) / (a*e- d*b), 我們將這兩個公式定義成巨集便可以求出各種聯立方程式的解, 程式如下 : 50 25
解二元一次聯立方程式 51 解二元一次聯立方程式 52 26
解二元一次聯立方程式 53 執行結果 54 27
程式執行說明 第 4 5 行利用巨集分別定義解 x y 的公式 第 11~14 行由螢幕輸入巨集定義的計算所需要的變數值 第 28 29 行以 float 型別的變數 x y 分別去接巨集 XX YY 的計算結果 55 巨集與函式的綜合應用 : 匯率換算 利用定義巨集與定義常數的方法, 我們可以定義各種貨幣的匯率以及換算的公式 在以下的範例中, 所定義的匯率常數都是以 1 元新台幣為基準 任何兩種貨幣的換算也將先換成新台幣後, 再轉成對應的幣值 程式如下 : 56 28
匯率換算 57 匯率換算 58 29
匯率換算 59 匯率換算 60 30
匯率換算 61 執行結果 62 31
程式執行說明 第 2~7 行, 將新台幣對各種貨幣的匯率定義成常數 第 8 行, 定義貨幣間匯率轉換的計算公式 第 9 行, 宣告傳回各種貨幣匯率值的函式原型 第 10 行, 宣告傳回各種貨幣英文縮寫與中文對照結果的函式原型 63 程式執行說明 第 28~32 行, 如執行結果, 將貨幣轉換結果顯示在螢幕上 第 37~48 行, 會依照使用者的選擇, 傳回巨集名稱 ( 貨幣的縮寫代碼 ) 然後再依據所定義的巨集內容, 置換成相對的匯率 第 50~67 行, 會依照使用者的選擇, 輸出貨幣的名稱 64 32
請更正以下錯誤 : 練習 (1) #define F(x)2*x-3=1 (2) #define F(x,y) 2*x-1 (3) #define F=2*x+1 請用巨集定義下列敘述 : F=10 #define F 10 F(x)=2*x #define F(x) 2*x F(x,y)=2*y+3*x #define F(x,y) 2*y+3*x F(x,y)=y 2 +x 3 #define F(x,y) y*y+x*x*x 65 作業 試寫一程式以巨集定義圓週率及半徑平方算式, 程式輸入半徑, 求圓面積及體積 試寫一程式, 以巨集定義計算下面式子, 到第 n 項的和, n 由鍵盤輸入 (1*2)+(2*3)+ +(n*(n+1)) 試寫一程式, 以巨集定義計算下面式子, 到第 n 項的和, n 由鍵盤輸入 1/1+1/2+1/3+ +1/n 試寫一程式, 以巨集定義計算階乘的值提示 :F(n,i)=n-i,n 為項次 i 每次減 1 66 33