第 6 章  函數與巨集 函數簡介  遞迴函數  變數的儲存等級  前置處理器與巨集  上機實習課程
函數的原型宣告、定義與呼叫 (1)  函數原型宣告 函數原型宣告的語法格式如下: 例如一個函數 sum() 可接收兩筆成績參數,並傳回其最後計算總和值,原型宣告如下:  6-1  函數簡介   傳回資料型態 函數名稱 ( 資料型態 參數 1,  資料型態 參數 2, ……….); 或 傳回資料型態 函數名稱 ( 資料型態 ,  資料型態 ,  ……….); int sum(int score1,int score2) ; 或是 int sum(int, int) ;
函數的原型宣告、定義與呼叫 (2) 函數的定義 清楚了函數的原型宣告後,接下來我們要來知道定義函數主體的架構。 自訂函數在 C 中的定義方式與 main() 函數類似,基本架構如下:  6-1  函數簡介   函數型態 函數名稱 ( 資料型態 參數 1,  資料型態 參數 2, ……….) { 函數主體 ; : return 傳回值 ; }
函數名稱是準備定義函數的第一步,是由設計者自行來命名,命名規則與變數命名規則相符,最好能具備可讀性。  函數主體是由 C 的指令組成,在程式碼撰寫的風格上,我們建議使用註解來說明函數的作用。 如果函數型態宣告為 void ,則最後的 return 關鍵字可省略,或保留 return ,但其後不接傳回值,如: 6-1  函數簡介   return ;
函數的原型宣告、定義與呼叫 (3) 函數呼叫 函數呼叫的方式有兩種,假如沒有傳回值,通常直接使用函數名稱即可呼叫函數。 語法格式如下: 如果函數有傳回值,則可運用指定運算子 "=" 將傳回值指定給變數。如下所示: 函數名稱 ( 引數 1,  引數 2, ……….); 6-1  函數簡介   變數 = 函數名稱 ( 引數 1,  引數 2, ……….);
函數的實作練習 (1) 兩筆成績求和函數 sum() 以下程式範例是整合上述 sum() 函數中,包括函數原型宣告、定義與呼叫的完整設計,我們將輸入兩筆成績,並透過函數中的計算,列印出兩筆的總和。 程式範例: sum() 函數的實作練習: CH06_01.c 6-1  函數簡介
6-1  函數簡介
執行結果  程式解說  6-1  函數簡介   在第 4 行中宣告函數原型於 #include 引入檔後,主程式之前,也可以宣告在第 7 行的大括號後,此函數的參數有 2 個。 第 17~23 行則是函數定義的程式區塊部分。 當我們在第 10 行中讀入兩數 x 、 y 時,在第 11 行呼叫 sum() 函數,第 17 行中的引數 score1 會接收 x 的值, score2 會接收 y 的值。
函數的實作練習 (2) 求取某數的某次方值 以下程式範例是計算所輸入兩數 x 、 y 的 xy 值函數 Pow() ,定將函數定義放在 main() 函數之前,這樣的做法可以將函數宣告為所謂的「全域範圍」 。  程式範例:求取某數的某次方值實作練習: CH06_02.c  6-1  函數簡介
6-1  函數簡介
執行結果 程式解說  6-1  函數簡介   在第 7~15 行中,我們定義了函數的主體,由於是在 main() 函數之前,所以不需再進行函數原型宣告。 而第 23 行中的 scanf() 函數中是用「 ^ 」字元來作為輸入間隔字元。
函數的實作練習 (3) 畫出長方形圖形函數 以下程式範例是讓使用者輸入兩個數來計算長方形面積,並以’ *’ 畫出長方形圖形。 在本程式中並沒有傳回值,各位只要使用 return 指令即可。 程式範例:畫出長方形圖形函數實作練習: CH06_03.c 6-1  函數簡介
6-1  函數簡介
6-1  函數簡介
執行結果 程式解說  6-1  函數簡介   在第 5 行中宣告無傳回值有參數列的 draw_rect() 函數原型,而第 13 、 15 行中則輸入長與寬的值,並在第 16 行中呼叫 rect_display() 函數。 在第 26 行處還要設定所輸入的數字不得小於等於零,避免印不出圖形。
函數內的函數原型宣告 程式範例:函數內的函數原型宣告實作練習: CH06_04.c  6-1  函數簡介
6-1  函數簡介
執行結果 程式解說  6-1  函數簡介   在第 6 、 7 行中分別宣告了 f_abs() 與 cubic_abs() 的函數原型,因此在 main() 函數中可呼叫這些函數。 而在第 20 行中,因為要在第 21 行的 cubic_abs() 函數中呼叫 f_abs() 函數,所以必須先進行 f_abs() 函數的原型宣告。 在函數的應用中,如果函數間彼此要相互呼叫,必須在函數中補上即將呼叫的函數原型宣告。
全域變數   一個全域變數的使用時機應該像是圓周率的設定,例如您可以如下宣告一個變數: 由於圓周率是固定的,所以我們可以將之設定為全域變數,以利所有的函數來使用,而為了避免使用者更改了圓周率的值,我們還可使用了 const 關鍵字,表示這個變數一經設定之後就不得更改其值。  變數的有效範圍 (1) 6-1  函數簡介   const float pi = 3.14159;
變數的有效範圍 (2) 區域變數   區域變數只限於函數之中存取,離開該函數之後就失去作用。 以下程式範例是宣告了一個全域變數 x ,並呼叫 setX1() 函數時,將 x 值設定為 20 ,這時全域變數 x 的值成為 20 。 而於 setX2() 函數中則又宣告了一個區域變數 x ,則於函數執行期間,區域變數將暫時覆蓋全域變數,但在函數執行完畢後,又恢復全域變數 x=20 的值。 6-1  函數簡介
變數的有效範圍 (3) 程式範例:全域變數與區域變數的宣告與實作: CH06_05.c  6-1  函數簡介
6-1  函數簡介
執行結果 程式解說  6-1  函數簡介   在第 5 行中宣告全域變數 x ,第 9 、 10 行中則宣告 set1X() 與 set2X 的函數原型,當在 12 行中呼叫 set1X() 函數時,第 22 行中的 x 仍為全域變數,在第 23 、 13 行中都會輸出 20 。 但在 14 行中呼叫 set2X() 函數時,由於第 28 行所宣告的 x 為此程式中的區域變數,因此 28 行中會輸出 30 ,但是一旦離開此函數, x 則仍為全域變數了,第 15 行中還是會輸出 20 。
參數的傳遞 (1)  傳值呼叫 傳值呼叫的函數宣告型式如下所示: 傳值呼叫的函數呼叫型式如下所示: 6-1  函數簡介   回傳資料型態 函數名稱 ( 資料型態 參數 1,  資料型態 參數 2, ……….); 或 回傳資料型態 函數名稱 ( 資料型態 ,  資料型態 , ……….); 函數名稱 ( 引數 1, 引數 2, ……….);
參數的傳遞 (2) 傳址呼叫 傳址方式的函數宣告型式如下所示: 傳址呼叫的函數呼叫型式如下所示:   6-1  函數簡介   回傳資料型態 函數名稱 ( 資料型態  * 參數 1,  資料型態  * 參數 2, ……….); 或 回傳資料型態 函數名稱 ( 資料型態  *,  資料型態  *, ……….); 函數名稱 (& 引數 1,& 引數 2, ……….);
參數的傳遞 (3) 程式範例:傳值呼叫與傳址呼叫的宣告與實作: CH06_06.c 6-1  函數簡介
執行結果 6-1  函數簡介
程式解說  6-1  函數簡介   有關傳值呼叫 CallByValue() 函數與之前作法相同。 在第 22 行進行傳址呼叫的參數指定時,我們必須使用 & 「取址運算子」來取出變數 x 的記憶體位址,而在第 12 行要指定值時,則必須使用 * 「取值運算子」,告知編譯器將引數指定至參數 x 所指向的位址。
參數的傳遞 (4) 程式範例: add() 函數的傳址呼叫宣告與實作: CH06_07.c 6-1  函數簡介
執行結果  6-1  函數簡介
程式解說  6-1  函數簡介   在第 3 行中宣告傳址呼叫的函數原型宣告,而第 10 行中則將參數 a 與 b 的位址傳遞到第 17 行中 add() 函數,並由 * 「取值運算子」,告知編譯器將引數 p1 、 p2 指定至參數所指向的位址。
參數的傳遞 (5) 程式範例: exchange() 函數的傳址呼叫宣告與實作: CH06_08.c  6-1  函數簡介
6-1  函數簡介
執行結果 程式解說  6-1  函數簡介   在第 4 行中建立傳址呼叫函數 exchange() 的原型宣告,第 13 行中則由主程式中取得兩個數值。 接下來於第 13 行中呼叫 ecxhange() 函數,並傳遞兩個參數位址給該函數。 第 25~26 行則進行數值的交換。
參數的傳遞 (6) 程式範例: Average() 函數的傳值與傳址呼叫同步實作範例: CH06_09.c  6-1  函數簡介
6-1  函數簡介
執行結果 程式解說  6-1  函數簡介   在第 4 行中的 average() 函數原型宣告中,同時具備了傳值與傳址呼叫兩種方式的參數。 第 11 行則可輸入兩筆成績,分別存入 Chi 與 Eng 兩個變數。 第 14 行中則呼叫 average() 函數。 第 25 行中則由變數 c 與主程式中的 Ave 共用一位址。
遞迴函數  定義如下:  6-2  遞迴函數   假如一個函數或副程式,是由自身所定義或呼叫的,就稱為遞迴  (Recursion)  ,它至少要定義 2 種條件,包括一個可以反覆執行或呼叫的過程,與一個跳出執行過程的出口。
遞迴的定義 可以區分為以下兩種: 直接遞迴 (Direct Recursion) 指遞迴函數中,允許直接呼叫該函數本身,稱為直接遞迴 (Direct Recursion) 。 間接遞迴 指遞迴函數中,如果呼叫其他遞迴函數,再從其他遞迴函數呼叫回原來的遞迴函數,我們就稱做間接遞迴 (Indirect Recursion) 。 6-2  遞迴函數
階乘函數實作 (1) 3 階乘等於 3×2×1=6, 而 0 階乘則定義為 1 。 我們一般以符號“!”來代表階乘。 如 4 階乘可寫為 4! 。 任何問題想以遞迴式來表示,一般需要符合兩個條件:一個反覆的過程,以及一個跳出執行的缺口。 秉持這兩個原則, n! 可以寫成: 6-2  遞迴函數   n!=n×(n-1)*(n-2)……*1
階乘函數實作 (2) 程式範例:階乘函數 (n!) 的實作範例: CH06_10.c  6-2  遞迴函數
6-2  遞迴函數
執行結果  程式解說  6-2  遞迴函數   在第 11 行中輸入要計算的階乘數。 第 13~14 行中將列印出 1! 到 n! 的所有結果。 第 23 行中是設定跳出遞迴反覆執行過程中的缺口。 第 26 行中則是執行遞迴程式的過程。
基本定義: 從費伯那序列的定義,也可以嘗試把它轉成遞迴的形式: 費伯那序列實作 (1)  6-2  遞迴函數
費伯那序列實作 (2) 程式範例:費伯那序列的實作範例: CH06_11.c  6-2  遞迴函數
6-2  遞迴函數
執行結果  程式解說  6-2  遞迴函數   第 18~27 行中定義了 fib() 函數,並在第 10 行中輸入 n 值。 第 21 、 23 行中,判斷是否為第 0 、 1 、 2 項的費式數列值,如不是則執行第 26 行,以遞迴式計算出第 n 項費式數列值。
auto 變數 (1)  使用 auto 表示要宣告自動變數( automatic variable ),自動變數也就是區域變數,當您在宣告變數時沒有指定型態修飾詞,就會自動設定為 auto 。 自動變數是在進入函數區塊後,系統才配置記憶體空間給該變數,當離開該函數區塊,便將所配罝的記憶體空間歸還給系統。 它的宣告語法為: 6-3  變數的儲存等級 auto  資料型態 變數名稱 = 初始值;
auto 變數 (2) 程式範例: auto 修飾詞變數的宣告範例: CH06_12.c 6-3  變數的儲存等級
6-3  變數的儲存等級
執行結果  程式解說  6-3  變數的儲存等級 在 mail() 函數中宣告 auto 變數 i ,這時的 auto 關鍵字也可省略,此時 i 為此函數中的區域變數。 請注意! auto 變數不可宣告在第 7 行之前,否則會發生錯誤。 第 20 、 25 行中也都宣告了 auto 變數,但還是僅能供該函數使用。
extern 變數 (1) 宣告語法: 例如底下的例子:  6-3  變數的儲存等級 extern  資料型態 變數名稱 = 初始值;
extern 變數 (2) 程式範例: extern 修飾詞變數的宣告範例: CH06_13.c 6-3  變數的儲存等級
執行結果  6-3  變數的儲存等級
程式解說  6-3  變數的儲存等級 在第 20 行中宣告了一個外部變數 PI ,如果要在 main() 函數中使用,那麼必須於第 7 行中宣告  PI  為 extern 變數,就可於第 13 行中將 PI 變數用於計算圓面積的公式計算。
register 變數 使用 register 識別字來宣告變數,該變數將使用 CPU 的暫存器來儲存,由於 CPU 的暫存器速度較快,因而可以加快變數存取的效率,它的宣告語法如下: 6-3  變數的儲存等級 register  資料型態 變數名稱 = 初始值;
以下是它的宣告語法: 在宣告靜態區域變數同時,如果各位沒有設定初始值的話,系統自動將靜態變數初始值設定為 0 ,而一般的變數初始值,在未設定初始值的情況下,則是一個不確定值。  static 變數 (1) 6-3  變數的儲存等級 static  資料型態 變數名稱 = 初始值;
static 變數 (2) 程式範例: static  修飾詞變數的宣告範例: CH06_14.c 6-3  變數的儲存等級
6-3  變數的儲存等級
執行結果 程式解說  6-3  變數的儲存等級 第 11 行中,輸入所要計算的階層數。 第 23 行則使用 static 來宣告變數 count ,注意到使用 static 宣告變數時,該變數將只被啟始一次,所以我們利用 static 宣告的變數於函數結束後可以保留變數值的特性,在第 26 行處以計算遞迴呼叫的次數。
#include 語法有兩種指定方式: 在 #include 之後使用角括號 <> ,前置處理器將至預設的系統目錄中尋找指定的檔案。 使用雙引號“”來指定檔案,則前置處理器會先尋找目前程式檔案的工作目錄中是否有指定的檔案。 #include  指令  6-4  前置處理器與巨集 #include < 檔案名稱 > #include &quot; 檔案名稱 &quot;
語法如下所示: 例如 #define 常用於以有意義的名稱取代某個常數,這和使用關鍵字 const 來定義常數類似,下面程式範例使用 #define 巨集指令來定義 PI 為 3.14159 。 不同之處在於「取代」的差別,在這兩行程式碼中,前置處理器並不會理會第一行程式碼, pi 只是一個變數名稱,然而前置處理器會將 PI 取代為 3.14159 。  #define 指令 (1)  6-4  前置處理器與巨集 #define  巨集名稱  表示式 const float pi = 3.14159; #define PI 3.14159
#define 指令 (2) 程式範例: #define 巨集的常數取代功能範例: CH06_15.c 6-4  前置處理器與巨集
執行結果 程式解說  6-4  前置處理器與巨集 第 3 行定義巨集來取代常數。 在進行編譯時,前置處理器會先行將程式中所有的 PI 置換為 3.14159 ,然後再進行程式的編譯。
#define 指令 (3) 程式範例: #define 巨集的字串取代功能範例: CH06_16.c 6-4  前置處理器與巨集
執行結果  程式解說  6-4  前置處理器與巨集 第 3 行中定義巨集指令為 Justin 字串 , 並跳行,而第 7 行中由於 AUTHOR 這是被雙引號“”包括住的字串,所以它被視為字串輸出,前置處理器並不會將它如巨集指令般展開。 至於第 9 行的 AUTHOR 並沒有使用雙引號包括,所以前置處理器將之展開為 &quot;Justin&quot; ,並跳行。
#define 指令 (4) 程式範例: #define 巨集的無引數函數取代功能範例: CH06_17.c  6-4  前置處理器與巨集
執行結果  6-4  前置處理器與巨集
程式解說  6-4  前置處理器與巨集 其實這個程式範例仍然是簡單的巨集取代動作,前置處理器會將所有的 NEWLINE  展開為 printf(&quot;\n&quot;) ,如第 9 、 11 行,而 DRAWLINE 會展開為 draw_line() 函數,如第 8 、 10 行。
#define 指令 (5) 程式範例: #define 巨集的有引數函數取代功能範例: CH06_18.c 6-4  前置處理器與巨集
執行結果 程式解說  6-4  前置處理器與巨集 第 4 行中定義了有引數的巨集函數 C(F) ,可做為攝氏溫度轉華氏之用,請注意在括號間不得有空白,否則空白之後會被視為表示式,而編譯時就會發生錯誤。 第 12 行中的 C(Fx) 在編譯時已被直接取代為 (Fx-32)*5/9 。
#define 指令 (6) 程式範例: #undef 語法的宣告與使用範例: CH06_19.c 6-4  前置處理器與巨集
執行結果  6-4  前置處理器與巨集
程式解說  6-4  前置處理器與巨集 在此程式中重複定義一個巨集名稱,因此在第 11 行處使用了 undef 指令解除第一次定義的 DIVIDE 巨集。 然後才重新於第 13 行定義 DIVIDE 。
條件編譯指令 (1)  #ifdef 條件編譯 #ifdef 用來判斷巨集名稱是否已經定義過,如果已定義則編譯區塊中的陳述句,否則就不進行編譯。語法如下所示: 6-4  前置處理器與巨集 #ifdef  巨集名稱 陳述句 #endif
條件編譯指令 (2) 程式範例: #ifdef 條件編譯語法的宣告與使用範例: CH06_20.c 6-4  前置處理器與巨集
執行結果  6-4  前置處理器與巨集
程式解說  6-4  前置處理器與巨集 由於中第 3 行 DEBUG 名稱已被定義,第 11~13 行 #ifdef…#endif 區塊中的程式碼才會被編譯。 如果您將第 3 行中的 DEBUG 定義移除再進行編譯,程式的結果顯示就不會再有除錯的內容。
條件編譯指令 (3) #ifndef 條件編譯 #ifndef 語法的作用與 #ifdef 正好相反,如果巨集名稱沒有定義,則會編譯 #ifndef 區塊中的陳述句,語法如下所示: 6-4  前置處理器與巨集 #ifndef  巨集名稱 陳述句 #endif
條件編譯指令 (4) 程式範例: #ifndef 條件編譯語法的宣告與使用範例: CH06_21.c  6-4  前置處理器與巨集
執行結果  6-4  前置處理器與巨集
程式解說  6-4  前置處理器與巨集 在這個程式中,如果已經自行定義 ON 與 OFF ,則 #ifndef 區塊中的程式碼將不會被編譯,區塊中的 #define 也不會被展開。 第 6~8 行與第 11~14 行中用來執行當 ON 與 OFF 未定義時的執行步驟。
條件編譯指令 (5) 程式範例: NULL 的重新定義範例: CH06_22.c 6-4  前置處理器與巨集
執行結果 程式解說  6-4  前置處理器與巨集 第 6~8 行中,如果 NULL 已定義,則輸出 NULL 的值。 第 9 行中解除 C 中對 NULL 的定義,而第 10~13 行則將 NULL 定義為 -1 。
條件編譯指令 (6) #if 、 #else 、 #elif 條件編譯 語法如下所示:  6-4  前置處理器與巨集
條件編譯指令 (7) 程式範例: NULL 的重新定義範例: CH06_23.c  6-4  前置處理器與巨集
執行結果 程式解說  6-4  前置處理器與巨集 第 6 行利用 #if 條件編譯來判斷,第 9 行則是利用 #else 條件編譯指令,不過必須搭配 #if 指令,形成和 if else 條件敘述類似的功能。
上機實習課程 (1) 上機實習範例: CH06_24.c 請設計一函數 square(x) ,讓使用者輸入實數 x 值,並傳回 x 2 的實數值。  6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (2) 上機實習範例: CH06_25.c 延續 CH06_24.c 範例,請再自行設計一函數 cubic(x) ,讓使用者輸入實數 x 值,並傳回 x 3 的實數值,此外還要設計一函數 F(x)=2 x 3 +3 x 2 ,輸出 F(125) 的值。  6-5  上機實習課程
6-5  上機實習課程
上機實習課程 (3) 執行結果 上機實習範例】: CH06_26.c  請各位以遞迴式來設計一河內塔函數,當輸入要移動的盤子數量時,能輸出所有移動過程。  6-5  上機實習課程
6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (4) 上機實習範例: CH06_27.c  請試著撰寫一個傳址呼叫長度轉換函數,由使用者輸入英呎及英吋,並同步轉換成公尺及公分。提示如下: 6-5  上機實習課程 1 英呎 =12 吋; 1 吋 =2.54 公分
6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (5) 上機實習範例: CH06_28.c 以下這個程式範例是利用 #define 指令定義一個巨集 FUNCTION 來計算 x3+5x2 的值,特別提醒您在巨集中所定義的 x 於主程式中仍須宣告。 6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (6) 上機實習範例: CH06_29.c 以下這個程式範例是利用 #define 指令定義一個簡單計算圓面積的巨集函數,並且可以傳遞半徑為參數,讓使用者輸入半徑即可算出圓面積。 6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (7) 上機實習範例: CH06_30.c 請設計以非遞迴方式 ( 建議利用 for 迴圈 ) 來計算 n! 的值。 6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (8) 上機實習範例: CH06_31.c 請各位先在外部檔案 myfun.h 中定義階乘函數 factorial() 及平方值函數 square() ,並將其含括到程式中,再利用這兩個函數來計算如下數列的值: 6-5  上機實習課程
6-5  上機實習課程
myfun.h  6-5  上機實習課程
執行結果 上機實習範例: CH06_32.c 在此函數中我們使用了兩種參數傳遞模式,傳址與傳址呼叫。 其中記錄加權比例的變數是使用傳址呼叫,當函數中變數改變時, main() 函數中也會同步改變。  6-5  上機實習課程
6-5  上機實習課程
6-5  上機實習課程
上機實習課程 (9) 執行結果 上機實習範例: CH06_33.c 這個範例會要求您設計一個輸入兩個數值,利用輾轉相除法計算最大公因數的函數。  6-5  上機實習課程
6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (10) 上機實習範例: CH06_34.c 用傳值呼叫的方式重新設計 CH06_33.c 程式,由主程式接收兩個參數,並將這兩個參數傳給 gcd() 函數進行最大公因數的運算,接著也可利用最大公因數來求取兩者最小公倍數。  6-5  上機實習課程
6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (11) 上機實習範例: CH06_35.c 在以下程式範例中,說明了雖然已經分別宣告了兩個相同的 Sum 變數,但是由於它們所處的程式區段及佔用的記憶體位址不同,所以它們之間並沒有關聯。 6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (12) 上機實習範例: CH06_36.c  這個程式範例主要是設計公尺轉英呎的函數,並讓您了解暫存器變數的宣告及使用方式。 基本上,對於一般的程式設計師而言,使用到暫存器變數的機會並不多。 6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (13) 上機實習範例: CH06_37.c  以下利用一個簡單的程式範例來為您說明在在迴圈中函數呼叫時,函數中 static 變數及 auto 變數的差異。 6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (14) 上機實習範例: CH06_38.c 以下程式範例中編譯器會將程式中所有 MAX(a,b) 的名稱,替換成定義的算式。 編譯器替換算式的同時,也會把引數 a 與 b 值代入替換之後的計算式中。  6-5  上機實習課程
執行結果  6-5  上機實習課程
上機實習課程 (15) 上機實習範例: CH06_39.c 以下程式範例說明當 #if 指令的運算式成立 (Use_MACRO == 1) 時,則第 7 行的定義巨集才會執行,而主函數 main() 即可以使用 MAX 巨集。  6-5  上機實習課程
執行結果  6-5  上機實習課程

第六章 函數與巨集

  • 1.
    第 6 章 函數與巨集 函數簡介 遞迴函數 變數的儲存等級 前置處理器與巨集 上機實習課程
  • 2.
    函數的原型宣告、定義與呼叫 (1) 函數原型宣告 函數原型宣告的語法格式如下: 例如一個函數 sum() 可接收兩筆成績參數,並傳回其最後計算總和值,原型宣告如下: 6-1 函數簡介 傳回資料型態 函數名稱 ( 資料型態 參數 1, 資料型態 參數 2, ……….); 或 傳回資料型態 函數名稱 ( 資料型態 , 資料型態 , ……….); int sum(int score1,int score2) ; 或是 int sum(int, int) ;
  • 3.
    函數的原型宣告、定義與呼叫 (2) 函數的定義清楚了函數的原型宣告後,接下來我們要來知道定義函數主體的架構。 自訂函數在 C 中的定義方式與 main() 函數類似,基本架構如下: 6-1 函數簡介 函數型態 函數名稱 ( 資料型態 參數 1, 資料型態 參數 2, ……….) { 函數主體 ; : return 傳回值 ; }
  • 4.
    函數名稱是準備定義函數的第一步,是由設計者自行來命名,命名規則與變數命名規則相符,最好能具備可讀性。 函數主體是由C 的指令組成,在程式碼撰寫的風格上,我們建議使用註解來說明函數的作用。 如果函數型態宣告為 void ,則最後的 return 關鍵字可省略,或保留 return ,但其後不接傳回值,如: 6-1 函數簡介 return ;
  • 5.
    函數的原型宣告、定義與呼叫 (3) 函數呼叫函數呼叫的方式有兩種,假如沒有傳回值,通常直接使用函數名稱即可呼叫函數。 語法格式如下: 如果函數有傳回值,則可運用指定運算子 &quot;=&quot; 將傳回值指定給變數。如下所示: 函數名稱 ( 引數 1, 引數 2, ……….); 6-1 函數簡介 變數 = 函數名稱 ( 引數 1, 引數 2, ……….);
  • 6.
    函數的實作練習 (1) 兩筆成績求和函數sum() 以下程式範例是整合上述 sum() 函數中,包括函數原型宣告、定義與呼叫的完整設計,我們將輸入兩筆成績,並透過函數中的計算,列印出兩筆的總和。 程式範例: sum() 函數的實作練習: CH06_01.c 6-1 函數簡介
  • 7.
  • 8.
    執行結果 程式解說 6-1 函數簡介 在第 4 行中宣告函數原型於 #include 引入檔後,主程式之前,也可以宣告在第 7 行的大括號後,此函數的參數有 2 個。 第 17~23 行則是函數定義的程式區塊部分。 當我們在第 10 行中讀入兩數 x 、 y 時,在第 11 行呼叫 sum() 函數,第 17 行中的引數 score1 會接收 x 的值, score2 會接收 y 的值。
  • 9.
    函數的實作練習 (2) 求取某數的某次方值以下程式範例是計算所輸入兩數 x 、 y 的 xy 值函數 Pow() ,定將函數定義放在 main() 函數之前,這樣的做法可以將函數宣告為所謂的「全域範圍」 。 程式範例:求取某數的某次方值實作練習: CH06_02.c 6-1 函數簡介
  • 10.
  • 11.
    執行結果 程式解說 6-1 函數簡介 在第 7~15 行中,我們定義了函數的主體,由於是在 main() 函數之前,所以不需再進行函數原型宣告。 而第 23 行中的 scanf() 函數中是用「 ^ 」字元來作為輸入間隔字元。
  • 12.
    函數的實作練習 (3) 畫出長方形圖形函數以下程式範例是讓使用者輸入兩個數來計算長方形面積,並以’ *’ 畫出長方形圖形。 在本程式中並沒有傳回值,各位只要使用 return 指令即可。 程式範例:畫出長方形圖形函數實作練習: CH06_03.c 6-1 函數簡介
  • 13.
  • 14.
  • 15.
    執行結果 程式解說 6-1 函數簡介 在第 5 行中宣告無傳回值有參數列的 draw_rect() 函數原型,而第 13 、 15 行中則輸入長與寬的值,並在第 16 行中呼叫 rect_display() 函數。 在第 26 行處還要設定所輸入的數字不得小於等於零,避免印不出圖形。
  • 16.
  • 17.
  • 18.
    執行結果 程式解說 6-1 函數簡介 在第 6 、 7 行中分別宣告了 f_abs() 與 cubic_abs() 的函數原型,因此在 main() 函數中可呼叫這些函數。 而在第 20 行中,因為要在第 21 行的 cubic_abs() 函數中呼叫 f_abs() 函數,所以必須先進行 f_abs() 函數的原型宣告。 在函數的應用中,如果函數間彼此要相互呼叫,必須在函數中補上即將呼叫的函數原型宣告。
  • 19.
    全域變數 一個全域變數的使用時機應該像是圓周率的設定,例如您可以如下宣告一個變數: 由於圓周率是固定的,所以我們可以將之設定為全域變數,以利所有的函數來使用,而為了避免使用者更改了圓周率的值,我們還可使用了 const 關鍵字,表示這個變數一經設定之後就不得更改其值。 變數的有效範圍 (1) 6-1 函數簡介 const float pi = 3.14159;
  • 20.
    變數的有效範圍 (2) 區域變數 區域變數只限於函數之中存取,離開該函數之後就失去作用。 以下程式範例是宣告了一個全域變數 x ,並呼叫 setX1() 函數時,將 x 值設定為 20 ,這時全域變數 x 的值成為 20 。 而於 setX2() 函數中則又宣告了一個區域變數 x ,則於函數執行期間,區域變數將暫時覆蓋全域變數,但在函數執行完畢後,又恢復全域變數 x=20 的值。 6-1 函數簡介
  • 21.
  • 22.
  • 23.
    執行結果 程式解說 6-1 函數簡介 在第 5 行中宣告全域變數 x ,第 9 、 10 行中則宣告 set1X() 與 set2X 的函數原型,當在 12 行中呼叫 set1X() 函數時,第 22 行中的 x 仍為全域變數,在第 23 、 13 行中都會輸出 20 。 但在 14 行中呼叫 set2X() 函數時,由於第 28 行所宣告的 x 為此程式中的區域變數,因此 28 行中會輸出 30 ,但是一旦離開此函數, x 則仍為全域變數了,第 15 行中還是會輸出 20 。
  • 24.
    參數的傳遞 (1) 傳值呼叫 傳值呼叫的函數宣告型式如下所示: 傳值呼叫的函數呼叫型式如下所示: 6-1 函數簡介 回傳資料型態 函數名稱 ( 資料型態 參數 1, 資料型態 參數 2, ……….); 或 回傳資料型態 函數名稱 ( 資料型態 , 資料型態 , ……….); 函數名稱 ( 引數 1, 引數 2, ……….);
  • 25.
    參數的傳遞 (2) 傳址呼叫傳址方式的函數宣告型式如下所示: 傳址呼叫的函數呼叫型式如下所示: 6-1 函數簡介 回傳資料型態 函數名稱 ( 資料型態 * 參數 1, 資料型態 * 參數 2, ……….); 或 回傳資料型態 函數名稱 ( 資料型態 *, 資料型態 *, ……….); 函數名稱 (& 引數 1,& 引數 2, ……….);
  • 26.
  • 27.
    執行結果 6-1 函數簡介
  • 28.
    程式解說 6-1 函數簡介 有關傳值呼叫 CallByValue() 函數與之前作法相同。 在第 22 行進行傳址呼叫的參數指定時,我們必須使用 & 「取址運算子」來取出變數 x 的記憶體位址,而在第 12 行要指定值時,則必須使用 * 「取值運算子」,告知編譯器將引數指定至參數 x 所指向的位址。
  • 29.
    參數的傳遞 (4) 程式範例:add() 函數的傳址呼叫宣告與實作: CH06_07.c 6-1 函數簡介
  • 30.
    執行結果 6-1 函數簡介
  • 31.
    程式解說 6-1 函數簡介 在第 3 行中宣告傳址呼叫的函數原型宣告,而第 10 行中則將參數 a 與 b 的位址傳遞到第 17 行中 add() 函數,並由 * 「取值運算子」,告知編譯器將引數 p1 、 p2 指定至參數所指向的位址。
  • 32.
    參數的傳遞 (5) 程式範例:exchange() 函數的傳址呼叫宣告與實作: CH06_08.c 6-1 函數簡介
  • 33.
  • 34.
    執行結果 程式解說 6-1 函數簡介 在第 4 行中建立傳址呼叫函數 exchange() 的原型宣告,第 13 行中則由主程式中取得兩個數值。 接下來於第 13 行中呼叫 ecxhange() 函數,並傳遞兩個參數位址給該函數。 第 25~26 行則進行數值的交換。
  • 35.
    參數的傳遞 (6) 程式範例:Average() 函數的傳值與傳址呼叫同步實作範例: CH06_09.c 6-1 函數簡介
  • 36.
  • 37.
    執行結果 程式解說 6-1 函數簡介 在第 4 行中的 average() 函數原型宣告中,同時具備了傳值與傳址呼叫兩種方式的參數。 第 11 行則可輸入兩筆成績,分別存入 Chi 與 Eng 兩個變數。 第 14 行中則呼叫 average() 函數。 第 25 行中則由變數 c 與主程式中的 Ave 共用一位址。
  • 38.
    遞迴函數 定義如下: 6-2 遞迴函數 假如一個函數或副程式,是由自身所定義或呼叫的,就稱為遞迴 (Recursion) ,它至少要定義 2 種條件,包括一個可以反覆執行或呼叫的過程,與一個跳出執行過程的出口。
  • 39.
    遞迴的定義 可以區分為以下兩種: 直接遞迴(Direct Recursion) 指遞迴函數中,允許直接呼叫該函數本身,稱為直接遞迴 (Direct Recursion) 。 間接遞迴 指遞迴函數中,如果呼叫其他遞迴函數,再從其他遞迴函數呼叫回原來的遞迴函數,我們就稱做間接遞迴 (Indirect Recursion) 。 6-2 遞迴函數
  • 40.
    階乘函數實作 (1) 3階乘等於 3×2×1=6, 而 0 階乘則定義為 1 。 我們一般以符號“!”來代表階乘。 如 4 階乘可寫為 4! 。 任何問題想以遞迴式來表示,一般需要符合兩個條件:一個反覆的過程,以及一個跳出執行的缺口。 秉持這兩個原則, n! 可以寫成: 6-2 遞迴函數 n!=n×(n-1)*(n-2)……*1
  • 41.
    階乘函數實作 (2) 程式範例:階乘函數(n!) 的實作範例: CH06_10.c 6-2 遞迴函數
  • 42.
  • 43.
    執行結果 程式解說 6-2 遞迴函數 在第 11 行中輸入要計算的階乘數。 第 13~14 行中將列印出 1! 到 n! 的所有結果。 第 23 行中是設定跳出遞迴反覆執行過程中的缺口。 第 26 行中則是執行遞迴程式的過程。
  • 44.
  • 45.
  • 46.
  • 47.
    執行結果 程式解說 6-2 遞迴函數 第 18~27 行中定義了 fib() 函數,並在第 10 行中輸入 n 值。 第 21 、 23 行中,判斷是否為第 0 、 1 、 2 項的費式數列值,如不是則執行第 26 行,以遞迴式計算出第 n 項費式數列值。
  • 48.
    auto 變數 (1) 使用 auto 表示要宣告自動變數( automatic variable ),自動變數也就是區域變數,當您在宣告變數時沒有指定型態修飾詞,就會自動設定為 auto 。 自動變數是在進入函數區塊後,系統才配置記憶體空間給該變數,當離開該函數區塊,便將所配罝的記憶體空間歸還給系統。 它的宣告語法為: 6-3 變數的儲存等級 auto 資料型態 變數名稱 = 初始值;
  • 49.
    auto 變數 (2)程式範例: auto 修飾詞變數的宣告範例: CH06_12.c 6-3 變數的儲存等級
  • 50.
  • 51.
    執行結果 程式解說 6-3 變數的儲存等級 在 mail() 函數中宣告 auto 變數 i ,這時的 auto 關鍵字也可省略,此時 i 為此函數中的區域變數。 請注意! auto 變數不可宣告在第 7 行之前,否則會發生錯誤。 第 20 、 25 行中也都宣告了 auto 變數,但還是僅能供該函數使用。
  • 52.
    extern 變數 (1)宣告語法: 例如底下的例子: 6-3 變數的儲存等級 extern 資料型態 變數名稱 = 初始值;
  • 53.
    extern 變數 (2)程式範例: extern 修飾詞變數的宣告範例: CH06_13.c 6-3 變數的儲存等級
  • 54.
    執行結果 6-3 變數的儲存等級
  • 55.
    程式解說 6-3 變數的儲存等級 在第 20 行中宣告了一個外部變數 PI ,如果要在 main() 函數中使用,那麼必須於第 7 行中宣告 PI 為 extern 變數,就可於第 13 行中將 PI 變數用於計算圓面積的公式計算。
  • 56.
    register 變數 使用register 識別字來宣告變數,該變數將使用 CPU 的暫存器來儲存,由於 CPU 的暫存器速度較快,因而可以加快變數存取的效率,它的宣告語法如下: 6-3 變數的儲存等級 register 資料型態 變數名稱 = 初始值;
  • 57.
    以下是它的宣告語法: 在宣告靜態區域變數同時,如果各位沒有設定初始值的話,系統自動將靜態變數初始值設定為 0,而一般的變數初始值,在未設定初始值的情況下,則是一個不確定值。 static 變數 (1) 6-3 變數的儲存等級 static 資料型態 變數名稱 = 初始值;
  • 58.
    static 變數 (2)程式範例: static 修飾詞變數的宣告範例: CH06_14.c 6-3 變數的儲存等級
  • 59.
  • 60.
    執行結果 程式解說 6-3 變數的儲存等級 第 11 行中,輸入所要計算的階層數。 第 23 行則使用 static 來宣告變數 count ,注意到使用 static 宣告變數時,該變數將只被啟始一次,所以我們利用 static 宣告的變數於函數結束後可以保留變數值的特性,在第 26 行處以計算遞迴呼叫的次數。
  • 61.
    #include 語法有兩種指定方式: 在#include 之後使用角括號 <> ,前置處理器將至預設的系統目錄中尋找指定的檔案。 使用雙引號“”來指定檔案,則前置處理器會先尋找目前程式檔案的工作目錄中是否有指定的檔案。 #include 指令 6-4 前置處理器與巨集 #include < 檔案名稱 > #include &quot; 檔案名稱 &quot;
  • 62.
    語法如下所示: 例如 #define常用於以有意義的名稱取代某個常數,這和使用關鍵字 const 來定義常數類似,下面程式範例使用 #define 巨集指令來定義 PI 為 3.14159 。 不同之處在於「取代」的差別,在這兩行程式碼中,前置處理器並不會理會第一行程式碼, pi 只是一個變數名稱,然而前置處理器會將 PI 取代為 3.14159 。 #define 指令 (1) 6-4 前置處理器與巨集 #define 巨集名稱 表示式 const float pi = 3.14159; #define PI 3.14159
  • 63.
    #define 指令 (2)程式範例: #define 巨集的常數取代功能範例: CH06_15.c 6-4 前置處理器與巨集
  • 64.
    執行結果 程式解說 6-4 前置處理器與巨集 第 3 行定義巨集來取代常數。 在進行編譯時,前置處理器會先行將程式中所有的 PI 置換為 3.14159 ,然後再進行程式的編譯。
  • 65.
    #define 指令 (3)程式範例: #define 巨集的字串取代功能範例: CH06_16.c 6-4 前置處理器與巨集
  • 66.
    執行結果 程式解說 6-4 前置處理器與巨集 第 3 行中定義巨集指令為 Justin 字串 , 並跳行,而第 7 行中由於 AUTHOR 這是被雙引號“”包括住的字串,所以它被視為字串輸出,前置處理器並不會將它如巨集指令般展開。 至於第 9 行的 AUTHOR 並沒有使用雙引號包括,所以前置處理器將之展開為 &quot;Justin&quot; ,並跳行。
  • 67.
    #define 指令 (4)程式範例: #define 巨集的無引數函數取代功能範例: CH06_17.c 6-4 前置處理器與巨集
  • 68.
    執行結果 6-4 前置處理器與巨集
  • 69.
    程式解說 6-4 前置處理器與巨集 其實這個程式範例仍然是簡單的巨集取代動作,前置處理器會將所有的 NEWLINE 展開為 printf(&quot;\n&quot;) ,如第 9 、 11 行,而 DRAWLINE 會展開為 draw_line() 函數,如第 8 、 10 行。
  • 70.
    #define 指令 (5)程式範例: #define 巨集的有引數函數取代功能範例: CH06_18.c 6-4 前置處理器與巨集
  • 71.
    執行結果 程式解說 6-4 前置處理器與巨集 第 4 行中定義了有引數的巨集函數 C(F) ,可做為攝氏溫度轉華氏之用,請注意在括號間不得有空白,否則空白之後會被視為表示式,而編譯時就會發生錯誤。 第 12 行中的 C(Fx) 在編譯時已被直接取代為 (Fx-32)*5/9 。
  • 72.
    #define 指令 (6)程式範例: #undef 語法的宣告與使用範例: CH06_19.c 6-4 前置處理器與巨集
  • 73.
    執行結果 6-4 前置處理器與巨集
  • 74.
    程式解說 6-4 前置處理器與巨集 在此程式中重複定義一個巨集名稱,因此在第 11 行處使用了 undef 指令解除第一次定義的 DIVIDE 巨集。 然後才重新於第 13 行定義 DIVIDE 。
  • 75.
    條件編譯指令 (1) #ifdef 條件編譯 #ifdef 用來判斷巨集名稱是否已經定義過,如果已定義則編譯區塊中的陳述句,否則就不進行編譯。語法如下所示: 6-4 前置處理器與巨集 #ifdef 巨集名稱 陳述句 #endif
  • 76.
    條件編譯指令 (2) 程式範例:#ifdef 條件編譯語法的宣告與使用範例: CH06_20.c 6-4 前置處理器與巨集
  • 77.
    執行結果 6-4 前置處理器與巨集
  • 78.
    程式解說 6-4 前置處理器與巨集 由於中第 3 行 DEBUG 名稱已被定義,第 11~13 行 #ifdef…#endif 區塊中的程式碼才會被編譯。 如果您將第 3 行中的 DEBUG 定義移除再進行編譯,程式的結果顯示就不會再有除錯的內容。
  • 79.
    條件編譯指令 (3) #ifndef條件編譯 #ifndef 語法的作用與 #ifdef 正好相反,如果巨集名稱沒有定義,則會編譯 #ifndef 區塊中的陳述句,語法如下所示: 6-4 前置處理器與巨集 #ifndef 巨集名稱 陳述句 #endif
  • 80.
    條件編譯指令 (4) 程式範例:#ifndef 條件編譯語法的宣告與使用範例: CH06_21.c 6-4 前置處理器與巨集
  • 81.
    執行結果 6-4 前置處理器與巨集
  • 82.
    程式解說 6-4 前置處理器與巨集 在這個程式中,如果已經自行定義 ON 與 OFF ,則 #ifndef 區塊中的程式碼將不會被編譯,區塊中的 #define 也不會被展開。 第 6~8 行與第 11~14 行中用來執行當 ON 與 OFF 未定義時的執行步驟。
  • 83.
    條件編譯指令 (5) 程式範例:NULL 的重新定義範例: CH06_22.c 6-4 前置處理器與巨集
  • 84.
    執行結果 程式解說 6-4 前置處理器與巨集 第 6~8 行中,如果 NULL 已定義,則輸出 NULL 的值。 第 9 行中解除 C 中對 NULL 的定義,而第 10~13 行則將 NULL 定義為 -1 。
  • 85.
    條件編譯指令 (6) #if、 #else 、 #elif 條件編譯 語法如下所示: 6-4 前置處理器與巨集
  • 86.
    條件編譯指令 (7) 程式範例:NULL 的重新定義範例: CH06_23.c 6-4 前置處理器與巨集
  • 87.
    執行結果 程式解說 6-4 前置處理器與巨集 第 6 行利用 #if 條件編譯來判斷,第 9 行則是利用 #else 條件編譯指令,不過必須搭配 #if 指令,形成和 if else 條件敘述類似的功能。
  • 88.
    上機實習課程 (1) 上機實習範例:CH06_24.c 請設計一函數 square(x) ,讓使用者輸入實數 x 值,並傳回 x 2 的實數值。 6-5 上機實習課程
  • 89.
    執行結果 6-5 上機實習課程
  • 90.
    上機實習課程 (2) 上機實習範例:CH06_25.c 延續 CH06_24.c 範例,請再自行設計一函數 cubic(x) ,讓使用者輸入實數 x 值,並傳回 x 3 的實數值,此外還要設計一函數 F(x)=2 x 3 +3 x 2 ,輸出 F(125) 的值。 6-5 上機實習課程
  • 91.
  • 92.
    上機實習課程 (3) 執行結果上機實習範例】: CH06_26.c 請各位以遞迴式來設計一河內塔函數,當輸入要移動的盤子數量時,能輸出所有移動過程。 6-5 上機實習課程
  • 93.
  • 94.
    執行結果 6-5 上機實習課程
  • 95.
    上機實習課程 (4) 上機實習範例:CH06_27.c 請試著撰寫一個傳址呼叫長度轉換函數,由使用者輸入英呎及英吋,並同步轉換成公尺及公分。提示如下: 6-5 上機實習課程 1 英呎 =12 吋; 1 吋 =2.54 公分
  • 96.
  • 97.
    執行結果 6-5 上機實習課程
  • 98.
    上機實習課程 (5) 上機實習範例:CH06_28.c 以下這個程式範例是利用 #define 指令定義一個巨集 FUNCTION 來計算 x3+5x2 的值,特別提醒您在巨集中所定義的 x 於主程式中仍須宣告。 6-5 上機實習課程
  • 99.
    執行結果 6-5 上機實習課程
  • 100.
    上機實習課程 (6) 上機實習範例:CH06_29.c 以下這個程式範例是利用 #define 指令定義一個簡單計算圓面積的巨集函數,並且可以傳遞半徑為參數,讓使用者輸入半徑即可算出圓面積。 6-5 上機實習課程
  • 101.
    執行結果 6-5 上機實習課程
  • 102.
    上機實習課程 (7) 上機實習範例:CH06_30.c 請設計以非遞迴方式 ( 建議利用 for 迴圈 ) 來計算 n! 的值。 6-5 上機實習課程
  • 103.
    執行結果 6-5 上機實習課程
  • 104.
    上機實習課程 (8) 上機實習範例:CH06_31.c 請各位先在外部檔案 myfun.h 中定義階乘函數 factorial() 及平方值函數 square() ,並將其含括到程式中,再利用這兩個函數來計算如下數列的值: 6-5 上機實習課程
  • 105.
  • 106.
    myfun.h 6-5 上機實習課程
  • 107.
    執行結果 上機實習範例: CH06_32.c在此函數中我們使用了兩種參數傳遞模式,傳址與傳址呼叫。 其中記錄加權比例的變數是使用傳址呼叫,當函數中變數改變時, main() 函數中也會同步改變。 6-5 上機實習課程
  • 108.
  • 109.
  • 110.
    上機實習課程 (9) 執行結果上機實習範例: CH06_33.c 這個範例會要求您設計一個輸入兩個數值,利用輾轉相除法計算最大公因數的函數。 6-5 上機實習課程
  • 111.
  • 112.
    執行結果 6-5 上機實習課程
  • 113.
    上機實習課程 (10) 上機實習範例:CH06_34.c 用傳值呼叫的方式重新設計 CH06_33.c 程式,由主程式接收兩個參數,並將這兩個參數傳給 gcd() 函數進行最大公因數的運算,接著也可利用最大公因數來求取兩者最小公倍數。 6-5 上機實習課程
  • 114.
  • 115.
    執行結果 6-5 上機實習課程
  • 116.
    上機實習課程 (11) 上機實習範例:CH06_35.c 在以下程式範例中,說明了雖然已經分別宣告了兩個相同的 Sum 變數,但是由於它們所處的程式區段及佔用的記憶體位址不同,所以它們之間並沒有關聯。 6-5 上機實習課程
  • 117.
    執行結果 6-5 上機實習課程
  • 118.
    上機實習課程 (12) 上機實習範例:CH06_36.c 這個程式範例主要是設計公尺轉英呎的函數,並讓您了解暫存器變數的宣告及使用方式。 基本上,對於一般的程式設計師而言,使用到暫存器變數的機會並不多。 6-5 上機實習課程
  • 119.
    執行結果 6-5 上機實習課程
  • 120.
    上機實習課程 (13) 上機實習範例:CH06_37.c 以下利用一個簡單的程式範例來為您說明在在迴圈中函數呼叫時,函數中 static 變數及 auto 變數的差異。 6-5 上機實習課程
  • 121.
    執行結果 6-5 上機實習課程
  • 122.
    上機實習課程 (14) 上機實習範例:CH06_38.c 以下程式範例中編譯器會將程式中所有 MAX(a,b) 的名稱,替換成定義的算式。 編譯器替換算式的同時,也會把引數 a 與 b 值代入替換之後的計算式中。 6-5 上機實習課程
  • 123.
    執行結果 6-5 上機實習課程
  • 124.
    上機實習課程 (15) 上機實習範例:CH06_39.c 以下程式範例說明當 #if 指令的運算式成立 (Use_MACRO == 1) 時,則第 7 行的定義巨集才會執行,而主函數 main() 即可以使用 MAX 巨集。 6-5 上機實習課程
  • 125.
    執行結果 6-5 上機實習課程