第五章 函式與巨集




            1
模組化的原則

    抽象化 (Abstraction)
    1. 由上而下逐步精緻化的結構式系統發展方式
    2. Implementation independent


    資訊隱藏 (Information Hiding)



    模組的獨立性
    1. 介面單純,功能獨立性的模組較容易發展,在團隊作業
       時這樣的優點更是明顯。
    2. 加強內聚程度 (cohesion) ,減少耦合程度 (coupling)
2
系統架構 - 模組化的重要性 (1)



    越複雜的問題,需要花更大的努力才能解決。
    IF C(P1) > C(P2) then E(P1) > E(P2)

    若可以將一個大的問題分成數個問題而個別的去解
    決,則所花的功夫變少的許多。
    E(P1+P2)>E(P1)+E(P2)

    結論 : 把複雜的問題,分成許多較簡單的問題,則
    所需的努力會減少許多


3
模組 / 物件
- Attribute
- Private Method
- Public Method / Interface




4
函式 Function

    若想在 C 語言中,順利且有效率地撰寫程式的話
    ,請您 一定要瞭解函式
    將每 種功能都分別寫在不同的函式裡,那麼程式
    將會更容易被理解,也更方便除錯
    請在較大的程式裡使用函數,以此作為建構程式
    的程式區塊




5
可重複使用

    Bug 分佈集中,易於維護

    可讀性

    隱藏實作方式

6
函式 (cont.)

    每 個函數都應該有單一和定義明確的用途
    呼叫函數時,可使用參數來傳遞給被呼叫函數 ;
    被呼叫函數可使用 return 關鍵字,將結果傳回給
    呼叫函數。
    函式宣告包含傳回值 型態 , 函式名稱 , 傳入參數
    1. int diff(int x, int y);
    2. void print_name(void);
       • 傳回值型態為 void 表示無傳回值
    一個函數可以有一個或數個輸入引數,或是不帶
    任何輸入引數,但卻只有一個函數傳回值
    (return value)

7
函數定義 (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
Declaration 宣告

Definition 定義




9
函式 (cont.)

     函式主體可以在 main() 函式之前或之後 ; 但在
     main() 之後者 , 須要在 main() 之前宣告函式原型
     函式原型可讓編譯程式確認函式呼叫中所使用的
     參數數目和型態是否正確
     函式內 的變數,除非經過特別宣告,否則一律為
     ”區域變數”,即不同函式內 可以使用相同的變
     數名稱,因為該變數只會在該函式中有效




10
參數傳遞

     呼叫函數時 , 傳遞參數的方式
     1. 傳值呼叫 Call by value
       • 傳變數的數值資料
     1. 傳址呼叫 Call by address
       • 傳變數的記憶體位址
       • 若想讓函數存取呼叫函數裡的變數時




11
範例 - 傳值呼叫

     #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
範例 - 數值交換 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
void enable_bit(unsigned short val,unsigned short mask ););
 void enable_bit(unsigned short val,unsigned short mask
void disable_bit(unsigned short val,unsigned short mask ););
 void disable_bit(unsigned short val,unsigned short mask
int 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
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
範例 - 數值交換 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
遞迴函式

遞迴 : 函式在其程式碼中呼叫自己
     #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
遞迴函式撰寫法則


     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
範例 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
標準函式庫

     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) 計算 xy




22
巨集


          23
23
前處理器 ( pre-processor )

#include
#define

條件式編譯:
 #ifdef … #else … #endif
 #ifndef
 #if defined



24
巨集 Macro


     前置處理器 #define 可以定義巨集名稱 敘述
     在 compile 前 ,#define 會先做”字串取代”的動
     作
     1. #define 敘述結尾不加分號




25
#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
Using Arguments with #define




27
範例 - 巨集



     #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
1. 當使用巨集代替函式並包含有運算式時,
        必須特別小心『運算子優先權』的問題,
        否則將發生下面這個範例的非預期狀況。



      盡量使用括號就對了!




29
範例 - 巨集

     #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
範例 - 巨集 (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
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
Standard Library for Embedded System


• 有”嵌入式 printf()” 嗎 ?
  – A: 印到哪?
  – B: LCD !
  – A: 我的產品是火星探測車,哪來的 LCD ?
  – B: 那嵌入式 malloc()/free() 總有吧,你不要跟我說你的火星
    探測車沒有記憶體…
  – A: 記憶體多大?起始位址在哪?


• 嵌入式系統的”多樣性”嗎?



  33
巨集、函式、常數的差別

     巨集與常數變數
     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
巨集、函式、常數的差別

     巨集與函式
     1. 使用巨集的方式也可以做到部分函式的功能,
        事實上巨集函式與普通函式在處理及效率上有
        著極大的差異。
     2. 在處理上,處理『巨集』是前置處理階段的工
        作,而編譯『函式』則是編譯階段的工作。在
        效能上,則可以分為時間與空間上的差別:




35
(1) 空間上的差異:使用巨集時,巨集將被取代為程
    式碼加入到程式中,因此,程式使用越多次巨集
    ,經由前置處理後,編譯器看到的程式碼也就越
    長,自然編譯出來的執行檔也就越大,執行時所
    佔用的記憶體空間也越大。而使用函式就不會發
    生這種現象,因為一個函式只會佔用一塊記憶體
    空間來存放函式定義,不論該函式被呼叫幾次,
    除了函式呼叫時所需要的堆疊之外,完全不會增
    加記憶體空間的負擔。
(2) 時間上的差異:由於呼叫函式及返回函式時,需
    要對堆疊進行疊入( push )與疊出( pop )的
    動作,因此會多花費一些時間。而使用巨集時,
    由於沒有這些動作,因此執行速度比較快。

36
條件式編譯

#ifdef XXX
   code statements
#else
  code statements
#endif

#if (XXX == 20)

#endif

37
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”
#endif


38
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
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
Lab #5




將 Lab #4 中,判斷某點是否在某個矩形範圍內 的功
  能更改為 function 。

Main.c, Button.h, Button.c

int get_L_T_W_H(int *L…) ;
int check_boundary(int x, int y) ;


41
Lab #5-2


Please implement power(x,n) function for
your 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

C程式-函式與巨集

  • 1.
  • 2.
    模組化的原則 抽象化 (Abstraction) 1. 由上而下逐步精緻化的結構式系統發展方式 2. Implementation independent 資訊隱藏 (Information Hiding) 模組的獨立性 1. 介面單純,功能獨立性的模組較容易發展,在團隊作業 時這樣的優點更是明顯。 2. 加強內聚程度 (cohesion) ,減少耦合程度 (coupling) 2
  • 3.
    系統架構 - 模組化的重要性(1) 越複雜的問題,需要花更大的努力才能解決。 IF C(P1) > C(P2) then E(P1) > E(P2) 若可以將一個大的問題分成數個問題而個別的去解 決,則所花的功夫變少的許多。 E(P1+P2)>E(P1)+E(P2) 結論 : 把複雜的問題,分成許多較簡單的問題,則 所需的努力會減少許多 3
  • 4.
    模組 / 物件 -Attribute - Private Method - Public Method / Interface 4
  • 5.
    函式 Function 若想在 C 語言中,順利且有效率地撰寫程式的話 ,請您 一定要瞭解函式 將每 種功能都分別寫在不同的函式裡,那麼程式 將會更容易被理解,也更方便除錯 請在較大的程式裡使用函數,以此作為建構程式 的程式區塊 5
  • 6.
    可重複使用 Bug 分佈集中,易於維護 可讀性 隱藏實作方式 6
  • 7.
    函式 (cont.) 每 個函數都應該有單一和定義明確的用途 呼叫函數時,可使用參數來傳遞給被呼叫函數 ; 被呼叫函數可使用 return 關鍵字,將結果傳回給 呼叫函數。 函式宣告包含傳回值 型態 , 函式名稱 , 傳入參數 1. int diff(int x, int y); 2. void print_name(void); • 傳回值型態為 void 表示無傳回值 一個函數可以有一個或數個輸入引數,或是不帶 任何輸入引數,但卻只有一個函數傳回值 (return value) 7
  • 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.
  • 10.
    函式 (cont.) 函式主體可以在 main() 函式之前或之後 ; 但在 main() 之後者 , 須要在 main() 之前宣告函式原型 函式原型可讓編譯程式確認函式呼叫中所使用的 參數數目和型態是否正確 函式內 的變數,除非經過特別宣告,否則一律為 ”區域變數”,即不同函式內 可以使用相同的變 數名稱,因為該變數只會在該函式中有效 10
  • 11.
    參數傳遞 呼叫函數時 , 傳遞參數的方式 1. 傳值呼叫 Call by value • 傳變數的數值資料 1. 傳址呼叫 Call by address • 傳變數的記憶體位址 • 若想讓函數存取呼叫函數裡的變數時 11
  • 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.
    範例 - 數值交換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.
    void enable_bit(unsigned shortval,unsigned short mask );); void enable_bit(unsigned short val,unsigned short mask void disable_bit(unsigned short val,unsigned short mask );); void disable_bit(unsigned short val,unsigned short mask int 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.
    Pointers: A FirstLook 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.
    範例 - 數值交換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.
    遞迴函式 遞迴 : 函式在其程式碼中呼叫自己 #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.
    遞迴函式撰寫法則 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.
    範例 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.
    標準函式庫 math.h 數學運算 stdlib.h 標準工具庫 time.h 時間 20
  • 21.
  • 22.
    math.h 數學運算 double sqrt(double x) 計算 x 的平方根 double sin(double x) double pow(double x, double y) 計算 xy 22
  • 23.
    巨集 23 23
  • 24.
    前處理器 ( pre-processor) #include #define 條件式編譯: #ifdef … #else … #endif #ifndef #if defined 24
  • 25.
    巨集 Macro 前置處理器 #define 可以定義巨集名稱 敘述 在 compile 前 ,#define 會先做”字串取代”的動 作 1. #define 敘述結尾不加分號 25
  • 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.
  • 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.
    1. 當使用巨集代替函式並包含有運算式時, 必須特別小心『運算子優先權』的問題, 否則將發生下面這個範例的非預期狀況。 盡量使用括號就對了! 29
  • 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.
    範例 - 巨集(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.
    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.
    Standard Library forEmbedded System • 有”嵌入式 printf()” 嗎 ? – A: 印到哪? – B: LCD ! – A: 我的產品是火星探測車,哪來的 LCD ? – B: 那嵌入式 malloc()/free() 總有吧,你不要跟我說你的火星 探測車沒有記憶體… – A: 記憶體多大?起始位址在哪? • 嵌入式系統的”多樣性”嗎? 33
  • 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.
    巨集、函式、常數的差別 巨集與函式 1. 使用巨集的方式也可以做到部分函式的功能, 事實上巨集函式與普通函式在處理及效率上有 著極大的差異。 2. 在處理上,處理『巨集』是前置處理階段的工 作,而編譯『函式』則是編譯階段的工作。在 效能上,則可以分為時間與空間上的差別: 35
  • 36.
    (1) 空間上的差異:使用巨集時,巨集將被取代為程 式碼加入到程式中,因此,程式使用越多次巨集 ,經由前置處理後,編譯器看到的程式碼也就越 長,自然編譯出來的執行檔也就越大,執行時所 佔用的記憶體空間也越大。而使用函式就不會發 生這種現象,因為一個函式只會佔用一塊記憶體 空間來存放函式定義,不論該函式被呼叫幾次, 除了函式呼叫時所需要的堆疊之外,完全不會增 加記憶體空間的負擔。 (2) 時間上的差異:由於呼叫函式及返回函式時,需 要對堆疊進行疊入( push )與疊出( pop )的 動作,因此會多花費一些時間。而使用巨集時, 由於沒有這些動作,因此執行速度比較快。 36
  • 37.
    條件式編譯 #ifdef XXX code statements #else code statements #endif #if (XXX == 20) #endif 37
  • 38.
    Configuration of yourprogram #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” #endif 38
  • 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.
    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.
    Lab #5 將 Lab#4 中,判斷某點是否在某個矩形範圍內 的功 能更改為 function 。 Main.c, Button.h, Button.c int get_L_T_W_H(int *L…) ; int check_boundary(int x, int y) ; 41
  • 42.
    Lab #5-2 Please implementpower(x,n) function for your 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

Editor's Notes

  • #3 傳識資訊股份有限公司 http://www.fitpi.com • 模組的可分解性 (Decomposability): 一個優良的設計方法必須要有助於將一個新盼問題分解成幾個小的問題,而個 別地去解 • 模組的可合成性 (Composability): 符合模組可合成性要求的設計方法必須能幫助設計者能夠自由地把幾個模組組 合起來而產生新的系統。 • 模組的可瞭解性 (Understandability): 如果有誰想要瞭解這個模組的話、只需單獨地看到這個模組的說明文件就可以 了解此模組的動作、目的與行為,最差的情況也應只是參考鄰近的少數幾個模組而已,無須翻遍整份的系統文件。 • 模組的連續性 (Continuity): 當系統的需求規格必須有少許的更動時,如果可以確保所需修改的只限於某一個單一的模組,或是少數幾個模組的話,這樣的設計方法就符合模組的連續性的要求 • 模組的保護性 (Protection): 要求當某個模組再執行時後發生不正常的錯誤狀況時,錯誤應該被侷限於這個模組內,不應影響到其他的模組。
  • #4 傳識資訊股份有限公司 http://www.fitpi.com 若不斷的將問題一直分解成小問題來解,是否需要投入的時間最少? • 模組數量一多,各個模組間的介面變多,處理的成本會增加。 • 需要有效的抑制介面所引起的複雜問題