Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
第五章 函式與巨集            1
模組化的原則    抽象化 (Abstraction)    1. 由上而下逐步精緻化的結構式系統發展方式    2. Implementation independent    資訊隱藏 (Information Hiding)    模組的...
系統架構 - 模組化的重要性 (1)    越複雜的問題,需要花更大的努力才能解決。    IF C(P1) > C(P2) then E(P1) > E(P2)    若可以將一個大的問題分成數個問題而個別的去解    決,則所花的功夫變少的...
模組 / 物件- Attribute- Private Method- Public Method / Interface4
函式 Function    若想在 C 語言中,順利且有效率地撰寫程式的話    ,請您 一定要瞭解函式    將每 種功能都分別寫在不同的函式裡,那麼程式    將會更容易被理解,也更方便除錯    請在較大的程式裡使用函數,以此作為建構程...
可重複使用    Bug 分佈集中,易於維護    可讀性    隱藏實作方式6
函式 (cont.)    每 個函數都應該有單一和定義明確的用途    呼叫函數時,可使用參數來傳遞給被呼叫函數 ;    被呼叫函數可使用 return 關鍵字,將結果傳回給    呼叫函數。    函式宣告包含傳回值 型態 , 函式名稱 ...
函數定義 (Definition)    定義一個函數的語法如下:      資料型態 函數名稱 ( 引數部份 )    {           函數內 部變數宣告           主體敘述      }     int diff(int ...
Declaration 宣告Definition 定義9
函式 (cont.)     函式主體可以在 main() 函式之前或之後 ; 但在     main() 之後者 , 須要在 main() 之前宣告函式原型     函式原型可讓編譯程式確認函式呼叫中所使用的     參數數目和型態是否正確 ...
參數傳遞     呼叫函數時 , 傳遞參數的方式     1. 傳值呼叫 Call by value       • 傳變數的數值資料     1. 傳址呼叫 Call by address       • 傳變數的記憶體位址       • ...
範例 - 傳值呼叫     #include <stdio.h>      #include <stdio.h>     int imin(int n, int m); // declaration      int imin(int n, i...
範例 - 數值交換 1     /* swap1.c -- first attempt at aaswapping function */      /* swap1.c -- first attempt at swapping functio...
void enable_bit(unsigned short val,unsigned short mask );); void enable_bit(unsigned short val,unsigned short maskvoid dis...
Pointers: A First Look      a pointer is a variable (or, more generally, a data      object) whose value is an address    ...
範例 - 數值交換 2     #include <stdio.h>      #include <stdio.h>     void interchange(int **u, int **v);      void interchange(i...
遞迴函式遞迴 : 函式在其程式碼中呼叫自己     #include <stdio.h>      #include <stdio.h>     void recu(int);      void recu(int);     int main...
遞迴函式撰寫法則     1) 找出遞迴關係式     2) 指出遞迴終止條件     1. 計算 N 階乘        • n! = n*(n-1)*…*2*1 if n > 0        •1                     ...
範例 2- 遞迴呼叫       n!=n*(n-1)*…*1           n! = n * (n-1)! if n >0             1             if n=0     long fac (( int n )...
標準函式庫     math.h 數學運算     stdlib.h 標準工具庫     time.h 時間20
21
math.h 數學運算     double sqrt(double x) 計算 x 的平方根     double sin(double x)     double pow(double x, double y) 計算 xy22
巨集          2323
前處理器 ( pre-processor )#include#define條件式編譯: #ifdef … #else … #endif #ifndef #if defined24
巨集 Macro     前置處理器 #define 可以定義巨集名稱 敘述     在 compile 前 ,#define 會先做”字串取代”的動     作     1. #define 敘述結尾不加分號25
#include <stdio.h>      #include <stdio.h>     #define TWO 22      #define TWO     #define OW "Consistency is the last ref...
Using Arguments with #define27
範例 - 巨集     #include <stdio.h>      #include <stdio.h>     #define IMIN(N,M) (((N)<(M))? (N) ::(M))      #define IMIN(N,M)...
1. 當使用巨集代替函式並包含有運算式時,        必須特別小心『運算子優先權』的問題,        否則將發生下面這個範例的非預期狀況。      盡量使用括號就對了!29
範例 - 巨集     #include <stdio.h>      #include <stdio.h>     #define SQUARE(X) X*X      #define SQUARE(X) X*X     #define PR...
範例 - 巨集 (cont.)     SQUARE(x+2)     1. x+2*x+2       #define SQUARE(x) (x)*(x)       #define SQUARE(x) (x)*(x)     100/SQU...
time.h 時間     typedef long time_t;      typedef long time_t;     time_t time(time_t *timer); /* 傳回系統時間 */      time_t time...
Standard Library for Embedded System• 有”嵌入式 printf()” 嗎 ?  – A: 印到哪?  – B: LCD !  – A: 我的產品是火星探測車,哪來的 LCD ?  – B: 那嵌入式 mal...
巨集、函式、常數的差別     巨集與常數變數     1. 我們可以使用 #define 來設定巨集名稱以便取        代程式中的常數,也可以用 const 來宣告一個        常數變數            #define PI...
巨集、函式、常數的差別     巨集與函式     1. 使用巨集的方式也可以做到部分函式的功能,        事實上巨集函式與普通函式在處理及效率上有        著極大的差異。     2. 在處理上,處理『巨集』是前置處理階段的工  ...
(1) 空間上的差異:使用巨集時,巨集將被取代為程    式碼加入到程式中,因此,程式使用越多次巨集    ,經由前置處理後,編譯器看到的程式碼也就越    長,自然編譯出來的執行檔也就越大,執行時所    佔用的記憶體空間也越大。而使用函式就...
條件式編譯#ifdef XXX   code statements#else  code statements#endif#if (XXX == 20)#endif37
Configuration of your program#define WITH_DEVICE_A//#define WITH_DEVICE_B#define VERSION 2#if (VERSION == 3)     #error ”n...
defined()   defined(name) - evaluates to 1 if name has been   defined   #if defined(DEBUG) is the same as #ifdef DEBUG   #...
Example   #if defined(CONFIG_TQM823M) || defined(CONFIG_TQM850M) ||       defined(CONFIG_TQM855M) || defined(CONFIG_TQM860...
Lab #5將 Lab #4 中,判斷某點是否在某個矩形範圍內 的功  能更改為 function 。Main.c, Button.h, Button.cint get_L_T_W_H(int *L…) ;int check_boundary(...
Lab #5-2Please implement power(x,n) function foryour homework1. ( 一般函數 & 遞迴函數 )     int power(int x, int n)     {         ...
Upcoming SlideShare
Loading in …5
×

C程式-函式與巨集

34,184 views

Published on

艾鍗學院C語言與資料結構課程講義

Published in: Education
  • Be the first to comment

C程式-函式與巨集

  1. 1. 第五章 函式與巨集 1
  2. 2. 模組化的原則 抽象化 (Abstraction) 1. 由上而下逐步精緻化的結構式系統發展方式 2. Implementation independent 資訊隱藏 (Information Hiding) 模組的獨立性 1. 介面單純,功能獨立性的模組較容易發展,在團隊作業 時這樣的優點更是明顯。 2. 加強內聚程度 (cohesion) ,減少耦合程度 (coupling)2
  3. 3. 系統架構 - 模組化的重要性 (1) 越複雜的問題,需要花更大的努力才能解決。 IF C(P1) > C(P2) then E(P1) > E(P2) 若可以將一個大的問題分成數個問題而個別的去解 決,則所花的功夫變少的許多。 E(P1+P2)>E(P1)+E(P2) 結論 : 把複雜的問題,分成許多較簡單的問題,則 所需的努力會減少許多3
  4. 4. 模組 / 物件- Attribute- Private Method- Public Method / Interface4
  5. 5. 函式 Function 若想在 C 語言中,順利且有效率地撰寫程式的話 ,請您 一定要瞭解函式 將每 種功能都分別寫在不同的函式裡,那麼程式 將會更容易被理解,也更方便除錯 請在較大的程式裡使用函數,以此作為建構程式 的程式區塊5
  6. 6. 可重複使用 Bug 分佈集中,易於維護 可讀性 隱藏實作方式6
  7. 7. 函式 (cont.) 每 個函數都應該有單一和定義明確的用途 呼叫函數時,可使用參數來傳遞給被呼叫函數 ; 被呼叫函數可使用 return 關鍵字,將結果傳回給 呼叫函數。 函式宣告包含傳回值 型態 , 函式名稱 , 傳入參數 1. int diff(int x, int y); 2. void print_name(void); • 傳回值型態為 void 表示無傳回值 一個函數可以有一個或數個輸入引數,或是不帶 任何輸入引數,但卻只有一個函數傳回值 (return value)7
  8. 8. 函數定義 (Definition) 定義一個函數的語法如下: 資料型態 函數名稱 ( 引數部份 ) { 函數內 部變數宣告   主體敘述 } int diff(int x, int y); int diff(int x, int y); int diff(int x, int y) // ANSI C int diff(int x, int y) // ANSI C {{ // begin function body // begin function body int z; int z; // declare local variable // declare local variable zz= xx--y; = y; return z; return z; }}8
  9. 9. Declaration 宣告Definition 定義9
  10. 10. 函式 (cont.) 函式主體可以在 main() 函式之前或之後 ; 但在 main() 之後者 , 須要在 main() 之前宣告函式原型 函式原型可讓編譯程式確認函式呼叫中所使用的 參數數目和型態是否正確 函式內 的變數,除非經過特別宣告,否則一律為 ”區域變數”,即不同函式內 可以使用相同的變 數名稱,因為該變數只會在該函式中有效10
  11. 11. 參數傳遞 呼叫函數時 , 傳遞參數的方式 1. 傳值呼叫 Call by value • 傳變數的數值資料 1. 傳址呼叫 Call by address • 傳變數的記憶體位址 • 若想讓函數存取呼叫函數裡的變數時11
  12. 12. 範例 - 傳值呼叫 #include <stdio.h> #include <stdio.h> int imin(int n, int m); // declaration int imin(int n, int m); // declaration int main(void) int main(void) {{ int x=50,y=100; int x=50,y=100; printf("The lesser of %d and %d is %d.n", printf("The lesser of %d and %d is %d.n", x, y, imin(x,y)); x, y, imin(x,y)); return 0; return 0; }} int imin(int x,int y) int imin(int x,int y) // definition // definition {{ return ((x<y)? xx::y); return ((x<y)? y); }}12
  13. 13. 範例 - 數值交換 1 /* swap1.c -- first attempt at aaswapping function */ /* swap1.c -- first attempt at swapping function */ #include <stdio.h> #include <stdio.h> void interchange(int u, int v); /* declare function */ void interchange(int u, int v); /* declare function */ int main(void) int main(void) {{ int xx==5, yy==10; int 5, 10; printf("Originally xx==%d and yy==%d.n", xx, ,y); printf("Originally %d and %d.n", y); interchange(x, y); interchange(x, y); printf("Now xx==%d and yy==%d.n", x, y); printf("Now %d and %d.n", x, y); return 0; return 0; }} void interchange(int u, int v) /* define function */ void interchange(int u, int v) /* define function */ {{ int temp; int temp; temp ==u; temp u; Originally x = 5 and y = 10. uu==v; v; Now x = 5 and y = 10. vv==temp; temp; }}13
  14. 14. void enable_bit(unsigned short val,unsigned short mask );); void enable_bit(unsigned short val,unsigned short maskvoid disable_bit(unsigned short val,unsigned short mask );); void disable_bit(unsigned short val,unsigned short maskint main(void) int main(void){{ unsigned short REG16=0xff03; unsigned short REG16=0xff03; printf("REG:%#xn",REG16); printf("REG:%#xn",REG16); enable_bit(REG16,0x18); enable_bit(REG16,0x18); printf("REG:%#xn",REG16); printf("REG:%#xn",REG16); disable_bit(REG16,0x18); disable_bit(REG16,0x18); printf("REG:%#xn",REG16); printf("REG:%#xn",REG16); return 0; return 0;}}void enable_bit(unsigned short val,unsigned short mask ) ) void enable_bit(unsigned short val,unsigned short mask{{ val |=mask; val |=mask; printf("FUNC_enable_bit:REG:%#xn",val); printf("FUNC_enable_bit:REG:%#xn",val);}}void disable_bit(unsigned short val,unsigned short mask ) ) void disable_bit(unsigned short val,unsigned short mask{{ val &=~mask; val &=~mask; printf("FUNC_disable_bit:REG:%#xn",val); printf("FUNC_disable_bit:REG:%#xn",val);}} 14
  15. 15. Pointers: A First Look a pointer is a variable (or, more generally, a data object) whose value is an address Address operator : & and * int *ptr; int *ptr; Int nurse = 22; Int nurse = 22; ptr = &nurse; /* pointer to nurse */ ptr = &nurse; /* pointer to nurse */ int val = *ptr; int val = *ptr; printf(“ptr=%p, addr of nurse=%p, ptr=%x”, printf(“ptr=%p, addr of nurse=%p, ptr=%x”, ptr,&nurse,*ptr); ptr,&nurse,*ptr); 15
  16. 16. 範例 - 數值交換 2 #include <stdio.h> #include <stdio.h> void interchange(int **u, int **v); void interchange(int u, int v); int main(void) int main(void) {{ int xx==5, yy==10; int 5, 10; printf("Originally xx==%d and yy==%d.n", x, y); printf("Originally %d and %d.n", x, y); interchange(&x, &y); /* send addresses to function */ interchange(&x, &y); /* send addresses to function */ printf("Now xx==%d and yy==%d.n", x, y); printf("Now %d and %d.n", x, y); return 0; return 0; }} void interchange(int **u, int **v) void interchange(int u, int v) {{ int temp; int temp; temp ==*u; temp *u; /* temp gets value that uupoints to */ /* temp gets value that points to */ *u ==*v; *u *v; *v ==temp; *v temp; }}16
  17. 17. 遞迴函式遞迴 : 函式在其程式碼中呼叫自己 #include <stdio.h> #include <stdio.h> void recu(int); void recu(int); int main(void) int main(void) {{ recu(1); recu(1); return 0; return 0; }} void recu(int n) void recu(int n) {{ printf("Level %dn", n); /* print #1 */ printf("Level %dn", n); /* print #1 */ if (n < 4) if (n < 4) recu(n+1); recu(n+1); printf("LEVEL %dn", n); /* print #2 */ printf("LEVEL %dn", n); /* print #2 */ }}17
  18. 18. 遞迴函式撰寫法則 1) 找出遞迴關係式 2) 指出遞迴終止條件 1. 計算 N 階乘 • n! = n*(n-1)*…*2*1 if n > 0 •1 if n=0 1. 費氏數列 • f(n)=f(n-1)+f(n-2) if n > 2 • f(n)=n if n<=2 1. 河內之塔18
  19. 19. 範例 2- 遞迴呼叫 n!=n*(n-1)*…*1 n! = n * (n-1)! if n >0 1 if n=0 long fac (( int n )) long fac int n {{ if (n <= 1) if (n <= 1) return 1; return 1; return n*fac(n-1) ;; return n*fac(n-1) }}19
  20. 20. 標準函式庫 math.h 數學運算 stdlib.h 標準工具庫 time.h 時間20
  21. 21. 21
  22. 22. math.h 數學運算 double sqrt(double x) 計算 x 的平方根 double sin(double x) double pow(double x, double y) 計算 xy22
  23. 23. 巨集 2323
  24. 24. 前處理器 ( pre-processor )#include#define條件式編譯: #ifdef … #else … #endif #ifndef #if defined24
  25. 25. 巨集 Macro 前置處理器 #define 可以定義巨集名稱 敘述 在 compile 前 ,#define 會先做”字串取代”的動 作 1. #define 敘述結尾不加分號25
  26. 26. #include <stdio.h> #include <stdio.h> #define TWO 22 #define TWO #define OW "Consistency is the last refuge of the unimagina #define OW "Consistency is the last refuge of the unimagina tive. - -Oscar Wilde" tive. Oscar Wilde" #define FOUR TWO*TWO #define FOUR TWO*TWO #define PX printf("X is %d.n", x) #define PX printf("X is %d.n", x) #define FMT "X is %d.n" #define FMT "X is %d.n" int main(void) int main(void) {{ int xx==TWO; int TWO; PX; PX; xx==FOUR; FOUR; printf(FMT, x); printf(FMT, x); printf("%sn", OW); printf("%sn", OW); printf("TWO: OWn"); printf("TWO: OWn"); return 0; return 0; }}26
  27. 27. Using Arguments with #define27
  28. 28. 範例 - 巨集 #include <stdio.h> #include <stdio.h> #define IMIN(N,M) (((N)<(M))? (N) ::(M)) #define IMIN(N,M) (((N)<(M))? (N) (M)) //#define IMIN(X,Y,Z) if(X<Y) Z=X; //#define IMIN(X,Y,Z) if(X<Y) Z=X; // // else Z=Y; else Z=Y; int main(void) int main(void) {{ int x=100,y=50; int x=100,y=50; printf("The lesser of %d and %d is %d.n", printf("The lesser of %d and %d is %d.n", x, y,IMIN(x,y)); x, y,IMIN(x,y)); return 0; return 0; }}28
  29. 29. 1. 當使用巨集代替函式並包含有運算式時, 必須特別小心『運算子優先權』的問題, 否則將發生下面這個範例的非預期狀況。 盡量使用括號就對了!29
  30. 30. 範例 - 巨集 #include <stdio.h> #include <stdio.h> #define SQUARE(X) X*X #define SQUARE(X) X*X #define PR(X) printf("The result is %d.n", X) #define PR(X) printf("The result is %d.n", X) int main(void) int main(void) {{ int xx==4; int 4; int z; int z; zz==SQUARE(x); SQUARE(x); PR(z); PR(z); zz==SQUARE(2); SQUARE(2); PR(z); PR(z); PR(SQUARE(x+2)); PR(SQUARE(x+2)); PR(100/SQUARE(2)); PR(100/SQUARE(2)); printf("x is %d.n", x); printf("x is %d.n", x); PR(SQUARE(++x)); PR(SQUARE(++x)); printf("After incrementing, xxis %x.n", x); printf("After incrementing, is %x.n", x); return 0; return 0; }}30
  31. 31. 範例 - 巨集 (cont.) SQUARE(x+2) 1. x+2*x+2 #define SQUARE(x) (x)*(x) #define SQUARE(x) (x)*(x) 100/SQUARE(2) 1. (100/2)*2 or 50*2 or 100 #define SQUARE(x) ((x)*(x)) #define SQUARE(x) ((x)*(x))31
  32. 32. time.h 時間 typedef long time_t; typedef long time_t; time_t time(time_t *timer); /* 傳回系統時間 */ time_t time(time_t *timer); /* 傳回系統時間 */ char *ctime(const time_t *timep); /* 印出時間字串 */ char *ctime(const time_t *timep); /* 印出時間字串 */ #include <stdio.h> #include <time.h> main() { time_t t = time(NULL); char* tn = ctime(&t); printf(" 現在時間: %sn", tn); }32
  33. 33. Standard Library for Embedded System• 有”嵌入式 printf()” 嗎 ? – A: 印到哪? – B: LCD ! – A: 我的產品是火星探測車,哪來的 LCD ? – B: 那嵌入式 malloc()/free() 總有吧,你不要跟我說你的火星 探測車沒有記憶體… – A: 記憶體多大?起始位址在哪?• 嵌入式系統的”多樣性”嗎? 33
  34. 34. 巨集、函式、常數的差別 巨集與常數變數 1. 我們可以使用 #define 來設定巨集名稱以便取 代程式中的常數,也可以用 const 來宣告一個 常數變數 #define PI 3.1415926 或 #define PI 3.1415926 或 const double PI=3.1415926; const double PI=3.1415926; 2. 雖然兩者都可以達到目的,但對於處理及編譯 過程則不太相同。對於使用 const 而言,由於 在程式編譯的過程中, const 宣告的常數變數 會被編譯器檢查資料型態34
  35. 35. 巨集、函式、常數的差別 巨集與函式 1. 使用巨集的方式也可以做到部分函式的功能, 事實上巨集函式與普通函式在處理及效率上有 著極大的差異。 2. 在處理上,處理『巨集』是前置處理階段的工 作,而編譯『函式』則是編譯階段的工作。在 效能上,則可以分為時間與空間上的差別:35
  36. 36. (1) 空間上的差異:使用巨集時,巨集將被取代為程 式碼加入到程式中,因此,程式使用越多次巨集 ,經由前置處理後,編譯器看到的程式碼也就越 長,自然編譯出來的執行檔也就越大,執行時所 佔用的記憶體空間也越大。而使用函式就不會發 生這種現象,因為一個函式只會佔用一塊記憶體 空間來存放函式定義,不論該函式被呼叫幾次, 除了函式呼叫時所需要的堆疊之外,完全不會增 加記憶體空間的負擔。(2) 時間上的差異:由於呼叫函式及返回函式時,需 要對堆疊進行疊入( push )與疊出( pop )的 動作,因此會多花費一些時間。而使用巨集時, 由於沒有這些動作,因此執行速度比較快。36
  37. 37. 條件式編譯#ifdef XXX code statements#else code statements#endif#if (XXX == 20)#endif37
  38. 38. Configuration of your program#define WITH_DEVICE_A//#define WITH_DEVICE_B#define VERSION 2#if (VERSION == 3) #error ”new function for version 3.0”#endif#ifdef WITH_DEVICE_B #warning “drivers of Device-B”#endif38
  39. 39. defined() defined(name) - evaluates to 1 if name has been defined #if defined(DEBUG) is the same as #ifdef DEBUG #if expression - if expression evaluates to a non- zero value, process the following code #if 0 #if 0 /*code which should not be compiled*/ /*code which should not be compiled*/ #endif #endif 7-39
  40. 40. Example #if defined(CONFIG_TQM823M) || defined(CONFIG_TQM850M) || defined(CONFIG_TQM855M) || defined(CONFIG_TQM860M) || defined(CONFIG_TQM862M) || defined(CONFIG_TQM866M) # ifndef CONFIG_TQM8xxM # define CONFIG_TQM8xxM # endif #endif #if defined(UNIX) || (defined(MYSYS) && (defined(SYS_VERSION) && ( (SYS_VERSION > 17)))) #endif gcc test.c -DMYSYS -DSYS_VERSION=19 7-40
  41. 41. Lab #5將 Lab #4 中,判斷某點是否在某個矩形範圍內 的功 能更改為 function 。Main.c, Button.h, Button.cint get_L_T_W_H(int *L…) ;int check_boundary(int x, int y) ;41
  42. 42. Lab #5-2Please implement power(x,n) function foryour homework1. ( 一般函數 & 遞迴函數 ) int power(int x, int n) { int power(int x, int n) int sum = 1 ; { int i ; if(n == 0) return 1 ; for(i=0;i<n;i++) else { { sum = sum*x ; } return x * power(x,n-1) ; } return sum ; } }42

×