• 沒有找到結果。

函式

N/A
N/A
Protected

Academic year: 2022

Share "函式"

Copied!
40
0
0

加載中.... (立即查看全文)

全文

(1)

講師: 李根逸 (Ken-Yi Lee), E-mail: feis.tw@gmail.com

函式

【第五講】

!153

(2)

課程⼤大綱

!154

函式宣告 [P.155]

函式呼叫 [P.156]

C 標準函式庫 [P.157]

數學函式庫 [P.158]

函式定義 [P.160]

定義數學函式 [P.163]

定義模組化函式 [P.166]

變數可視範圍 [P.167]

for 的⼩小括號內宣告變數 [P.170]

產⽣生亂數 [P.173]

產⽣生夠亂的亂數 [P.175]

函式遞迴 [P.176]

(3)

函式宣告

函式的概念來⾃自於數學。可以把每個函式想成是⼀一個 程式模組,包含⼀一段程式⽚片段,具有某種設計好的功 能。

!

函式宣告:

!

!

⼀一個函式可以接受零到多個參數輸⼊入,並回傳⼀一個值 在函式宣告時,參數名稱不是必要可以省略:

輸⼊入參數

函式

回傳值

回傳值的資料型態 函式名稱(參數資料型態 [參數名稱], ...); 


double sqrt(double x);

!155

double sqrt(double);

(4)

函式呼叫

函式呼叫語法:

函式名稱([引數值], ...) 範例:

呼叫函式時,會將函式需要的引數值算出後再執⾏行函 式。函式執⾏行完後則會回傳⼀一個回傳值型態的值 (暫 時變數)

!

!

在程式碼呼叫函式之前需要宣告或定義函式

!156

sqrt(4.0)

double s = sqrt(2.0*v);


-> double s = sqrt(2.0*2.0);


-> double s = sqrt(4.0);


-> double s = 2.0;

double v = 2.0;

(5)

C 標準函式庫

C 語⾔言有內建已經寫好的的函式庫,例如 sqrt (求 平⽅方根) 這類的數學函式 :

!

!

!

呼叫 sqrt 需引⽤用 <math.h> (內含 sqrt 宣告)

除此之外還有 printf、scanf 和 system 這種⾮非數 學函式 :

printf

“Hello world” 11

4.0

sqrt

2.0

!157

sqrt(4.0) == 2.0

printf(“Hello world”) == 11

(6)

<math.h> 數學函式庫

常⾒見的數學函式 說明

double sqrt(double x ) 回傳 x 的平⽅方根 double exp(double x) 回傳 e 的 x 次⽅方

double log(double x) 回傳 x 的⾃自然對數 (底為 e) double fabs(double x) 回傳 x 的絕對值

double ceil(double x) 回傳不⼩小於 x 的最⼩小整數 double floor(double x) 回傳不太於 x 的最⼤大整數 double pow(double x, double y) 回傳 x 的 y 次⽅方

double fmod(double x, double y) 回傳 x 除以 y 的浮點餘數 double sin(double x) 回傳 x 的正弦值

double cos(double x) 回傳 x 的餘弦值 double tan(double x) 回傳 x 的正切值

[查詢函式] Windows: MSDN 網站, Linux 或 Unix-like: man 指令

!158

(7)

【補充】 C 的標準函式庫

<assert.h> 協助偵錯函式

<ctype.h> 字元處理

<errno.h> 錯誤處理函式

<float.h> 浮點數相關定義

<limits.h> 資料型態屬性

<locale.h> 地區相關

<math.h> 數學

<setjmp.h> 在函式間跳躍

!159

<signal.h> 訊號 (signal) 處理

<stdarg.h> 使⽤用不固定個數函式參

<stddef.h> 標準定義

<stdio.h> 輸⼊入輸出

<stdlib.h> ⼀一般函式

<string.h> 字串處理

<time.h> 時間計算

(8)

函式定義

函式定義語法 :

!

!

同⼀一函式只能定義⼀一次,且不可在函式内定義函式

範例 : max2(int,int) 是⼀一個會回傳兩整數最⼤大值 的函式

回傳值的資料型態 函式名稱(參數資料型態 參數名稱, ...) {
 /* 程式碼內容 */


return 回傳值;
 }

int max2(int x, int y) {
 if (x >= y) {


return x;


} else {


return y;


}
} main 也是⼀一個函式,會在程式⼀一開始執⾏行時被呼叫

!160

(9)

/* 求兩整數最⼤大值 */


#include <stdio.h>


#include <stdlib.h>


int max2(int x, int y) {
 if (x >= y) {


return x;


} else {


return y;


}
} 


int main() {
 int a, b;


printf("請輸⼊入⼀一個整數:");


scanf("%d", &a);


printf("請輸⼊入另⼀一個整數: ");


scanf("%d", &b);


printf("其中⽐比較⼤大的整數是 %d\n", max2(a, b));


system("pause");


return 0;


}

將 定 義 放 置 在 呼 叫 前 可以省去宣告的動作 函式定義

(函式的真正內容所在)

函式呼叫 (執⾏行指定的函式)

!161

(10)

/* 求兩整數最⼤大值 */


#include <stdio.h>


#include <stdlib.h>


int max2(int, int);

int main() {
 int a, b;


printf("請輸⼊入⼀一個整數:");


scanf("%d", &a);


printf("請輸⼊入另⼀一個整數: ");


scanf("%d", &b);


printf("其中⽐比較⼤大的整數是 %d\n", max2(a, b));


system("pause");


return 0;


}

int max2(int x, int y) {
 if (x >= y) {


return x;


} else {


return y;


}
}

函式宣告

(跟變數⼀一樣,在呼叫函 式之前我們需要先宣告)

函式呼叫 (執⾏行指定的函式)

函式定義

(函式的真正實作所在)

將定義放置在呼叫前後 都可以,但是在呼叫前

⼀一定要宣告或定義

函式呼叫時會先將參數 的值算出後再傳⼊入函式

!162

【思考】為什麼要分成宣告跟定義?

(11)

!163

【範例】定義數學函式

試寫⼀一名為 myabs 的函式,傳⼊入⼀一個整數後回傳該 整數的絕對值

!

!

!

!

試寫⼀一名為 mypow 的函式,傳⼊入底數 x 與指數 y 兩個浮點數 (double) 後,回傳 x 的 y 次⽅方是多

【範例】 mypow.cpp

【範例】 myabs.cpp

(12)

【範例】⽐比⼤大⼩小

試寫⼀一函式 max3 讓使⽤用者輸⼊入三個數字,並回傳 三個數字中最⼤大的

提⽰示 : 可以利⽤用 max2 來幫你完成!

!164

【範例】max3.cpp

(13)

main

max3

傳⼊入引數

回傳值

max2

max2

!165 max3(3, 5, 4)

int max3(int a, int b, int c) {
 return max2(max2(a, b), c);


}

max2(3, 5)

max2(5, 4)

3, 5, 4

5

3, 5

5, 4 5

5

可以利⽤用呼叫其他函式來完成

⼀一個函式是 C 語⾔言的重要⼿手法

(14)

【範例】定義模組化函式

修改範例,將『畫 N 個星星』模組化

這裡我們寫⼀一個函式

void draw_stars(int len) 來

len 個星星

Please enter an integer: 3


*
**


***

Please enter an integer: 5


*
**


***
****


*****


void 是個特殊的資料型 態,代表虛無的意思,

這裡⽤用在不需要回傳值 的函式。當回傳值型態 void 時,return 後不接回傳值。

!166

【範例】triangle.cpp

(15)

變數可視範圍 (scope)

我們可以依照變數宣告在程式碼的位置分為兩種變數:

全域變數 (global variable)

宣告在函式外。因為容易造成名稱污染,請避免使⽤用全域變數

區域變數 (local variable)

宣告在區塊内 (成對的左右⼤大括號)

⼀一個變數可以被某段程式碼使⽤用要同時滿⾜足兩個條件:

該變數是全域變數或宣告該變數的區塊包含這段程式碼 這段程式碼位於該變數宣告以後

變數⽣生命週期

:

全域變數:程式開始時配置記憶體,程式結束時釋放 區域變數:宣告時配置記憶體,離開區塊時釋放

!167

(16)

#include <stdio.h>


#include <stdlib.h>


int i = 5;

void p(){


/* int i = -1; */ 


i = i + 1; 


printf("%d\n", i);


return;


}

int main(){


printf("%d\n", i);


int i = 6; 


i = i + 1;


p();


printf("%d\n", i); 


system("pause");


return 0;


}

這程式的執⾏行結果是 ?

!168

(17)

#include <stdio.h>


#include <stdlib.h>


int main(){


int i = 3;


printf("%d\n", i);


if (i == 3) {
 i = i + 1;


int i = 6; 


printf("%d\n", i); 


i = i + 1;


}


if (i == 3) {


printf("%d\n", i);


}


system("pause");


return 0;


}

這程式的執⾏行結果是 ?

!169

(18)

在迴圈前宣告的變數,在迴圈結束後⼀一樣可以存取:

!

!

!

C99 或 C++ 我們可以在 for ⼩小括號的初始式內 宣告變數,此時的變數在迴圈執⾏行過程中指的是同⼀一 個,⽽而離開迴圈後該變數就離開可視範圍:

!170

int i = 1;


while (i <= 10) {


printf(“%d\n”, i);


i++;


}
printf(“%d\n”, i);

for (int i = 1; i <= 10; i++) {
 int j = i;


printf(“%d\n”, j);


}


printf(“%d\n”, i); /* 不合法 */

for 的⼩小括號内宣告變數

int i;


for (i = 1; i <= 10; i++) {
 printf(“%d\n”, i);


}
 printf(“%d”, i);

這個特性可以讓 for 計數⽤用的 變數不會污染到後⾯面的程式碼

(19)

!171

【補充】靜態修飾字 (static)

除了之前所提的全域變數和區域變數外,我們在變數 前可以加上 static 修飾字:

加在全域變數前:

該變數的⽣生命週期不變

該變數的可視範圍由全程式變成該檔案本⾝身

加在區域變數前:

該變數的⽣生命週期與全域變數相同,於整個程式開始時配置,結束 時釋放

該變數從程式開始到結束只會初始化⼀一次 該變數的可視範圍不變

【範例】triangle.cpp

(20)

【補充】函式的引數預設值

我們在定義函式的參數時可以給予預設值:

例如 :

!

當呼叫 max3(3, 4) 時,就會如同呼叫 max3(3, 4, 0)

注意的是,在有多個參數時,前⾯面的參數如果設定有預設值的話,

後⾯面的參數也⼀一定要有:

這個是不合法的:

!172

int max3(int a, int b, int c = 0) { ... }

int max3(int a, int b = 0, int c) { ... }

(21)

產⽣生亂數

C 標準函式庫中包含了⼀一個能產⽣生整數亂數的函式 :

int

rand(): 回傳⼀一⻑⾧長整數型態的亂數 [宣告於 stdlib.h]

每次呼叫 rand() 函式都會得到隨機的回傳值

#include <stdio.h>


#include <stdlib.h>


int main() {


for (int i = 1; i <= 5; i++) {


printf(“產⽣生的亂數: %d\n”, rand());


}


system(“pause”);


return 0;


}

!173

(22)

【範例】擲骰⼦子

試寫⼀一程式讓電腦亂數產⽣生⼗十個⼀一到六之間的數字,

模擬擲骰⼦子的結果

提⽰示: rand() 的回傳值的範圍是 [0, RAND_MAX],我 們可以利⽤用求餘數 (%) 運算⼦子來求取某固定範圍的整 數亂數

!174

【範例】dice.cpp

(23)

產⽣生夠亂的亂數

電腦產⽣生亂數是利⽤用內定的⼀一個亂數表,因此我們必 須利⽤用 <stdlib.h> 內的 srand 函式適當的選取第

⼀一個開始的亂數種,否則亂數出現的順序將是固定可 預測的

可以⽤用 srand(time(0)) 指令初始化亂數表

void srand(unsigned int seed):⽤用 seed 初始化亂數表 time_t time(time_t *):回傳⺫⽬目前時間 [宣告於 time.h]

int main(){


srand(time(0));


printf(“A random number: %d\n”, rand());


system(“pause”);


return 0;


}

!175

(24)

函式遞迴

遞迴是函式應⽤用上最迷⼈人⽽而容易困惑的⼀一種觀念 函式遞迴指的是在函式定義内⼜又呼叫同名函式 :

test 函式定義內⼜又呼叫 test,試想這樣會產⽣生怎 樣的結果

void test() {
 test();


return;


}


int main() {
 test();


return 0;


}


!176

(25)

main

test test test

1: void test() {
 2: test();


3: return;


4: }


5:
6: int main() {
 7: test();


8: return 0;


9: }


!177 test() test() test() test()

注意這裡每個 test 都是分別存在的

(26)

【範例】數數字

[1]

試想⼀一下下列程式碼會產⽣生怎樣的結果?

#include <stdio.h>


#include <stdlib.h>

void countdown(int count) {
 printf("%d\n", count);


if (count != 0) {


countdown(count-1);


}


return;


}

int main() {


countdown(2);


system("pause");


return 0;


}

!178

【範例】countdown_1.cpp

(27)

!179

main

2

1

0

countdown

countdown

countdown(2)

countdown(1)

countdown

countdown(0)

(28)

int main() {


!180

countdown(2);


return 0;


}
 void countdown(int count=2) {
 printf("%d\n", count);


if (count != 0) {


countdown(count-1);


}


return;


} void countdown(int count=1) {
 printf("%d\n", count);


if (count != 0) {


countdown(count-1);


}


return;


} void countdown(int count=0) {
 printf("%d\n", count);


if (count != 0) {


countdown(count-1);


}


return;


}

輸出:


2
1
 0

(29)

【範例】數數字

[2]

試想⼀一下下列程式碼會產⽣生怎樣的結果?

#include <stdio.h>


#include <stdlib.h>

void countdown(int count) {
 if (count != 0) {


countdown(count-1);


}


printf("%d\n", count);


return;


}

int main() {


countdown(2);


system("pause");


return 0;


}

!181

【範例】countdown_2.cpp

(30)

int main() {


!182

countdown(2);


return 0;


}
 void countdown(int count=2) {
 if (count != 0) {


countdown(count-1);


}


printf("%d\n", count);


return;


} void countdown(int count=1) {
 if (count != 0) {


countdown(count-1);


}


printf("%d\n", count);


return;


} void countdown(int count=0) {
 if (count != 0) {


countdown(count-1);


}


printf("%d\n", count);


return;


}

輸出:


0
1
 2

(31)

【範例】求數字和

試寫⼀一 sum 的函式去計算 1+2+...+N 的值

sum(N) == N + sum(N-1) 且 sum(1) == 1

!

!

!

!

!

!

可以⽤用迴圈 (while 或 for) 來寫嗎 ?

int sum(int N) {
 if (N == 1) {
 return 1;


} else {


return N + sum(N-1);


}
 }


!183

【範例】sum.cpp

(32)

【補充】C++ 的函式重載

C 語⾔言裡⾯面,同樣名字的函式,只能有⼀一個

C++ 語⾔言裡⾯面,同樣名字的函式,只要參數個數 或 型 別 不 同 就 可 以 有 很 多 個 , 稱 為 函 式 重 載 (function overloading)

!184

void print(int v) { printf(“%d\n”, v); }
 void print(double v) { printf(“%f\n”, v); }


print(3); // 呼叫 print(int)


print(3.0); // 呼叫 print(double)


(33)

習題 [1]

[E0501] 試寫⼀一函式 max4 讓使⽤用者輸⼊入四個整 數,並回傳四個數字中最⼤大的

請試著寫出上⾯面兩例在執⾏行時呼叫函式的歷程?

[E0502] 試寫⼀一個 round 函式,傳回輸⼊入參數四 捨五⼊入⾄至整數位後的值

int round(float num) { ... }

數學函式庫 (math.h) 中有⼀一 floor 函式可以將輸⼊入 參數無條件捨去

[E0503] 試寫⼀一個程式讓電腦擲 6000 次骰⼦子,並 統計⼀一到六各個數字分別出現了幾次

!185

(34)

習題 [2]

[E0504] 試寫⼀一程式,產⽣生⼀一 0 ⾄至 9 之間整數亂 數,讓使⽤用者猜測該數字為何,直到使⽤用者猜對。

[E0505] 試寫⼀一程式,輸⼊入⼀一元⼆二次⽅方程式的三個 參數 (a, b, c),求該⼀一元⼆二次⽅方程式的實數解

無實數解時請印出警告訊息 ax2+bx+c = 0

<math.h> 内有 sqrt 函式可以⽤用來平⽅方根值

!186

(35)

習題 [3]

(遞迴)

[E0506] 試⽤用遞迴函式寫⼀一程式,讓使⽤用者輸⼊入⼀一 正整數 N 後顯⽰示 N! 的值

N! = 1 * 2 * 3 * ... * N = (N-1)! * N

[E0507] Fibonacci 數列 :

0, 1, 1, 2, 3, 5, 8, 13, 21 ...

規則 :

數列中的前兩個數是 0 和 1

數列中的其他的數是前兩個數字的和

試寫⼀一遞迴程式去計算第 N 個的 Fibonacci 數是多少

!187

(36)

!188

(37)

!189

(38)

!190

(39)

!191

(40)

!192

參考文獻

相關文件

Cauchy 積分理論是複變函數論中三個主要組成部分之一, 有了 Cauchy 積分理論, 複變 函 數論才形成一門獨立的學科, 並且導出一系列在微積分中得不到的結果。 我們先從 Cauchy

但是讀者還是應該可以揣測出 n 重積分的 Fubini 定理...

All rights reserved.. 1

對於給定的一個 x 值,經過某一對應方式後得到「唯一」的 y 值,這種對應方式我們稱 為函數,其中 x 是自變數,y 是應變數。. 而在表

前一章我們學過了一次函數,本章將繼續延伸到二次函數。二次函數的函數圖形為拋

前一章我們學過了一次函數,本章將繼續延伸到二次函數。二次函數的函數圖形為拋

全球新冠肺炎感染人數持續增加,面對 Delta

強制轉型:把 profit轉換成double的型態