C/C++‎ > ‎

函式、遞迴函數

2012/11/20 謝碧景(c)編製更新 
一般程式中會將特定功能或經常需重複使用之程式編撰成獨立的小單元,稱為程序,主程式可呼叫此程序,在C++即稱此程序為函式(function),其可傳回或不傳回返回值,而在呼叫函式過程中亦可傳遞參數或不傳遞參數。此外 C++ 還提供內建函式,例如:Math數學函式、亂數函式、char字元處理函式、轉換函式(字串轉數值函式、數值轉字串函式)等。
*註:在其他語言中若無傳回值之程序稱為副程式(subroutine),有傳回值之程序即稱為函式(function)。

一、自訂函式

(一)建立函式

 回傳值資料型別 函式名稱(參數列)                  //參數列可有可無,接收呼叫函式傳進的

  {

            程式碼;

     …

     return(運算式);                        //返回值

  }



右範例,執行結果:
 範例:
 #include <iostream>
 using namespace std;
 void test()                            //自訂函數test,void 無傳回值
 {
       cout<<"Hello! I'm a function.\n";
 }
 int main()                             //主程式
 {
       test();                           //呼叫函數test
       system("PAUSE"); 
       return 0;
 }
註:
    1. 函式(function)會執行指定的動作,可傳回或不傳回返回值。
    2. return 返回呼叫函式,會傳回返回值,其資料型別必須和回傳值資料型別的宣告相同;若不傳回任何值,回傳值之資料型別宣告 void,則 return 可省略。
    3. 參數列或稱引數列(argument)接收呼叫函式傳進的值,須宣告與呼叫函式對應引數之資料型別相同,參數可為常數、變數、陣列、結構、物件、使用者自 定資料型別;若無傳入值,參數列可省略,但()小括號不可省略
    4. 函式庫代表函數集合,善用函式庫可簡化程式的開發。可利用標頭檔(副檔名為.h)集合所設計的函數,再使用#include前置處理器於程式前端加入標頭檔(header file)。當程式原始碼以編譯為執行檔之前,會先加入標頭檔進行編譯,再編譯程式內容。
    5. .h檔案是一份文件,若設計的函式庫不想被別人看到內容,請編譯為Static Library,它會產生一個二進位檔案,則設計的函式庫內容即可避免外流。
(二)呼叫函式

在 main() 函式(即主程式或稱主函式)中呼叫自訂函式,即會執行自訂函式,若函式有傳回值,可以同型別之變數儲存返回值,如下:

 回傳值資料型別變數=函式名稱(參數串列)         //參數列亦稱引數(argument)又稱實參數(Actual Argument)


 範例1:以函式設計,輸入攝氏(Celsius)溫度,並轉換華氏(Fahrenheit)溫度輸出。(檔名:func-ex1.cpp)

執行結果



(三)宣告函式原型

如上範例,如果自訂函式定義寫在 main() 主程式的上方,呼叫時不用宣告函式的原型;若自訂函式定義寫在 main() 主程式的下方,則呼叫自訂函式時必須宣告函式的原型,否則會產生編譯錯誤。注意:結束的【;】符號,而參數名稱可省略,一般會將函式的原型宣告放在檔頭宣告之後。

 回傳值資料型別 函式名稱(參數1 資料型別1,參數2 資料型別2…);


 範例2:同上範例。(檔名:func-ex2.cpp)



 範例3:求矩形面積。

  • 方法Ⅰ:自訂函式定義寫在 main() 主程式的下方,則呼叫自訂函式時必須宣告函式的原型。 (檔名:func-ex3-1.cpp)

執行結果


  • 方法Ⅱ:自訂函式定義寫在 main() 主程式的上方,呼叫時不用宣告函式的原型。 (檔名:func-ex3-2.cpp)


(四)參數

  1. 傳值呼叫(call by value)
  2. 傳參考呼叫(call by reference)
  3. 傳址呼叫( call by address))
(五)遞迴 (Recursive)
函式本身呼叫自己的函式即為遞迴,撰寫時函式中必須有結束點否則程式會形成無窮迴圈造成錯誤。

 範例4: 計算自然數的階層,n!【提示:n!=n*(n-1)*(nN-2)*...*2*1 ,例如:7!=7*6*5*4*3*2*1,3!=3*2*1,而0!=1】 (檔名:func-ex4.cpp)

執行結果


(六)多載 (overloading)

函式的命名允許使用多個相同的名稱,並透過傳遞參數列或參數的型別來區別不同的方法,稱為『函式多載』。

 範例5:建立Area()函式,以多載方式分別求正方形和矩形面積。

執行結果



二、內建函式

C++內建許多功能強大的函式,您僅需引用該函式的標頭檔即可使用該函式。

數學函數大都定義在 math.h 標頭檔,故使用這類函數時必須引用 #include <math.h>到程式的開頭,在數學運算中提供三角函數、指數、對數等函式,並定義了部分數學常數。

  • 常用數學函數:

函式

功能說明

範例  float x,y,n;

 abs(x)

 絕對值

語法:float abs(float x);

說明:傳回 x 絕對值

 cin>>x;                              //若輸入-123

 cout<<abs(x);                   //顯示 123

 pow(x,n)

 次方

語法:float pow(float x,float n);

說明:傳回 xn

 cin>>x>>n;                        //若輸入 2  10

 cout<<pow(x,n);                //顯示 1024

 sqrt(x)

 平方根

語法:float sqrt(float x);

說明:傳回 √x 值(x≧0)

 cin>>x;                               //若輸入 169

 cout<<sqrt(x);                    //顯示 13

 hypot(x,y)

語法:float hypot(float x,float y);

說明:傳回 √x2+y2

 cin>>x>>y                         //若輸入 11  3

 cout<<hypot(x,y);              //顯示 11.4018

 max(x,y) 語法:max(x,y)

 取得 x 與 y 中較大的值

 float n=max(12,35);            //n=35

 min(x,y)

語法:min(x,y)

 取得 x 與 y 中較小的值

 float n=min(12,35);             //n=12

 sin(x)

 cos(x)

 tan(x)

三角函數

語法:float sin(float x);

            float cos(float x);

            float tan(float x);

說明:傳回 三角函數值

            x以弳度量(弧度)為單位

            (角度度量)*π/180=弳度量(π=3.14159)

 asin(x)

 acos(x)

 atan(x)

反三角函數

語法:float asin(float x);

            float acos(float x);

            float atan(float x);

說明:傳回 反三角函數值

           asin與 acos的x範圍為 -1≦x≦1

 asin(float x); 取得參數的反正弦函數值

 float n=asin(0.5);      //n=π/6=0.523599

 acos(float x); 取得參數的反餘弦函數值

 float n=acos(0.5);     //n=π/3=1.0472

 atan(float x); 取得參數的反正切函數值

 float n=atan(1.0);     //n=π/4=0.785398

 exp(x)

語法:float exp(float x);

傳回:ex

 取得自然對數的參數次方值,即e的x次方

 float n=exp(2.0);      //n=(2.71828)2=7.38906

 log(x)

語法:float log(float x);

傳回:ln(x)值

 取得參數的自然對數值

 float n=log(2.0);       //n=0.693147



 範例6:數學函式

執行結果




rand() 亂數函式會傳回大於等於0,小於32767 的整數,該函式會配合亂數種子seed 使用,為使亂數種子產生不規則的亂數,會以系統時間為主,而系統時間必須引用time.h標頭檔,語法如下:

 #include <time.h>

 srand((unsigned int) time(Null));


一般會以【%】餘數運算子產生某特定範圍的亂數,例如:產生 m <= r <= n 的亂數,公式如下:

  int r=m+rand()%(n-m+1);              // 產生 m <= r <= n 的亂數

 範例7:班級32位同學,以亂數產生5 個號碼。

執行結果



 範例8:骰子遊戲,以亂數產生1-6 的點數。

執行結果


*註:getch() 僅接受一個字元,且該字元不顯示在螢幕上。請參閱【補充:I/O 輸入、輸出應用】

 
(三) char 字元陣列函式

char型態的陣列稱為字元陣列,通常用來儲存字元字串, 字元陣列是由一串字元,最後加上一個字串的結束字元【\0】組成,即字元陣列宣告後會自動在陣列結尾加上【\0】字元,每一個字元大小是1個byte。

 char name[]="Dev-C++";                              // 利用字串初始化
  • 常用的 char 字元陣列函式,如下表:

  函式

功能說明

 範例

 strlen(s)

語法:strlen(s);

說明:傳回字串 s 的長度,不包含結束字元('\0')

 char s[]="Dev-C++";

 int n=strlen(s);                 // n=7

 strcmp(s1,s2)

語法:int strcmp(s1,s2);

說明:比較字串,若兩字串相等就傳回 0,若s1>s2 則傳回 1,若 s1<s2 則傳回 -1。

 char s1[]="Dev-C++";
 char s2[]="BASIC";
 int n=strcmp(s1,s2);        // n=1

 strcpy(s1,s2)

語法:strcpy(s1,s2);

說明:將 s2 字串的內容複製給 s1 字串。

 char s1[]="Dev-C++";
 char s2[]="BASIC";
 cout<<strcpy(s1,s2);       // BASIC

 strcat(s1,s2)

語法:strcat(s1,s2);

說明:結合 s1、s2字串,並指派給 s1字串。

 char s1[]="Dev-C++";
 char s2[]="BASIC";
 cout<<strcat(s1,s2);        // Dev-C++BASIC
 cout<<strlen(s1);             // 12

 tolower(s);

語法: tolower(s);

說明:將 s 字元轉換為小寫。

 char c=tolower('A');

 cout<<c;              // a

 toupper(s);

語法:toupper(s);

說明:將 s 字元轉換為大寫

 char c=toupper('a');
 cout<<c;                         // A


(四) 字串與數值型別之轉換函式

  函式

功能說明

範例

 字串轉數值函式:

 int atoi(s);

語法:atoi(s);

說明:將字串 s 轉換為整數。

 char s[]="1234";
 int n=atoi(s);          // n=1234

 float atof(s);

語法:atof(s);

說明:將字串 s 轉換為浮點數。

 char s[]="12.345";
 float n=atof(s);                 // n=12.345

 long atol(s);

語法:atol(s);

說明:將字串 s 轉換為長整數。

 char s[]="1234567";
 float n=atol(s);                 // n=1234567

 數值轉字串函式:

 itoa(n);

語法:itoa(n);

說明:將整數 n 轉換為字串。

 char s[10];
 int n=1234;
 itoa(n,s,sizeof(s));
 cout<<s<<endl;                // 1234

 ltoa(n);

語法: ltoa(n);

說明:將長整數 n 轉換為字串。

 char s[10];
 long n=1234567;
 ltoa(n,s,sizeof(s));
 cout<<s<<endl;                 // 1234567
 cout<<strlen(s)<<endl;;      // 7


三、補充:I/O 輸入、輸出應用

(一)輸入函式

1.輸入字元函式:getchar()、getche()、getch() 
  • 使用 C 的 getch()、getche() 必須引用 #include <conio.h> 標頭檔。
  • getchar()、getche()、getch() 三者輸入字元函式之比較表如下:
  getchar() getche() getch()
 結束時按【Enter】鍵 需要 不需要 不需要
 輸入字元顯示在螢幕上 不會

2.輸入字串函式:
  • gets() 允許連續輸入任何字元,一直到按下【Enter】鍵為止,系統自動在所讀取一系列字元後加入字串結束字元【\0】為空字元(即不佔字串長度),再將此字串放入指定的字元陣列中。
(二)輸出函式

1.出字元函式:
  • putchar() 將指定的字元變數,顯示到目前游標所在。

 範例9:利用 getchar()getche()、getch() 及 putchar() 函式來輸入及顯示字元。參閱 ASCII符號表 (檔名:func-ex9_getchar.cpp)

執行結果


*註:字元 A →ASCII值為 65,字元 a →ASCII值為 97,字元 1 →ASCII值為 49 (即十進位)。

2.輸出字串函式:
  • puts() 將字串顯示在螢幕上。

 範例10:比較輸入函式 gets() 和 cin 指令的差異,並將字串分別以 cout 與 puts() 顯示出。

執行結果



 範例練習

  • 函式
題1:以函式設計,輸入一個整數,求其絕對值

執行結果


題2以函式設計,輸入一個整數,平方根。參閱數學函式 sqrt(x) 

執行結果


題3以函式設計求 x 的 n 次方。參閱數學函式 pow(x,n) 次方函式設計 】

執行結果


題4以函式設計,輸入一正整數,並判斷此數是否為質數。

執行結果:
執行結果:

題5以函式設計,找出所有小於50000之正整數,其值等於其各個位數之階乘和。
例: 4!+0!+5!+8!+5!
  =24+1+120+40320+120
  =40585
 

執行結果


題6:以函式設計,銀行存款計算方式如下:
N天後的存款=開始存入的錢x(1+RATE/365)^N,其中RATE代表年利率。試寫一程式包含下列兩個功能:
(1)輸入開始的存款經過N天後,輸出存款為何?
(2)輸入N天後的存款,求開始時要存入多少錢?方法1pow(x,n)函數 ,參閱數學函式方法2方法3

執行結果


題7:以函式設計,A、B二正整數,若所有能整除A的正數之和等於B(包括1,但不包括A本身);反之亦然,則A、B二數稱為"friendly"
例如220及 284,其中能整除220之數有:1+2+4+5+10+11+20+22+44+55+110=284
能整除284的有:1+2+4+71+142=220
設計一程式: 找出所有成"friendly"關係的正整數組,且此正整數組中的兩個數均小 於500。 

執行結果


題8:以函式設計,輸入兩個正整數,並求其最大公因數 gcd及最小公倍數 lcm 提示:a*b兩數乘積等於gcd*lcm                                                                                       
方法1方法2

執行結果


  • 【進階題】以函式設計,輸入三正整數,求此三數之最大公因數與最小公倍數提示:利用gcd(gcd(a,b),c) 求三數之最大公因數
    方法1方法2

    執行結果

題9:以函式設計,身分證認證:確認一組身分證號碼是否合法?
(1)英文代號以下表轉換成數字 
     A=10 台北市    J=18 新竹縣    S=26 高雄縣 
     B=11 台中市    K=19 苗栗縣    T=27 屏東縣 
     C=12 基隆市    L=20 台中縣    U=28 花蓮縣 
     D=13 台南市    M=21 南投縣   V=29 台東縣 
     E=14 高雄市    N=22 彰化縣    *W=32 金門縣 
     F=15 台北縣    *O=35 新竹市  X=30 澎湖縣 
     G=16 宜蘭縣    P=23 雲林縣    Y=31 陽明山 
     H=17 桃園縣    Q=24 嘉義縣    *Z=33 連江縣 
     *I=34 嘉義市    R=25 台南縣 
 (2)英文轉成的數字, 個位數乘9再加上十位數 
 (3)各數字從右到左依次乘1、2、3、4....8 
 (4)求出(2),(3)之和 
 (5)求出(4)除10後之餘數,用10減該餘數,結果就是檢查碼,若餘數為0 則檢查碼就是0 
 例如: 身分證字號  A123456789

執行結果


  • 遞迴函式
題10以遞迴函式設計,求N!【提示:N!=N*(N-1)*(N-2)*...*2*1 】

執行結果


題11以遞迴函式設計,輸入一個整數,並反轉此數值輸出。 

執行結果

  
題12以遞迴函式設計,求 Fibonacci 費氏級數﹦0,1,1,2,3,5,8,13,… 即 fn=1    當n=1或 n=2, fn=fn-1+fn-2    當n>2

執行結果


ċ
func-ex9_getchar.cpp
(1k)
Jing Tw,
2012年11月20日 上午6:11