函數應用與封裝
掌握條件判斷與迴圈結構,讓程式具備決策能力與高效執行方式,實現更靈活的控制流程。
目標
- 理解函數的基本概念與語法。
- 學會如何定義函數、呼叫函數,並理解參數與回傳值的作用。
- 實作簡單的函數應用,並理解函數的模組化設計。
- 理解遞迴函數的概念與應用。
- 學會如何設計遞迴函數,並理解遞迴的執行過程。
- 實作遞迴函數解決問題,並理解遞迴與迴圈的差異。
- 理解如何將函數封裝成獨立的 API 模組。
- 學會使用標頭檔(Header File)與原始碼檔(Source File)來組織程式碼。
- 使用
clang
編譯多檔案專案,並測試幾何應用。
1. 函數
1.1 函數的基本概念
- 函數是什麼?
- 函數是一段可重複使用的程式碼,可以接受輸入(參數),並回傳輸出(回傳值)。
- 函數的優點:
- 提高程式碼的可讀性與可維護性。
- 減少重複程式碼。
- 模組化設計,方便測試與除錯。
- 函數的組成:
- 函數名稱:用來識別函數。
- 參數:函數的輸入值。
- 回傳值:函數的輸出值。
- 函數主體:函數的執行邏輯。
1.2 函數的語法
- 函數的定義:
回傳值類型 函數名稱(參數1類型 參數1, 參數2類型 參數2, ...) { // 函數主體 return 回傳值; // 回傳值必須與回傳值類型一致 }
- 範例:一個簡單的加法函數
int add(int a, int b) { return a + b; }
1.3 函數的呼叫
- 呼叫函數的方式:
回傳值類型 變數 = 函數名稱(參數1, 參數2, ...);
- 範例:呼叫
add
函數int result = add(3, 5); // result 會是 8
1.4 參數與回傳值
- 參數:函數的輸入值,可以是基本型別(如
int
,float
)或自訂型別。 - 回傳值:函數執行完畢後回傳的結果,如果不需要回傳值,可以使用
void
。 - 範例:無回傳值的函數
void print_hello() { printf("你好,世界!\n"); }
1.5 函數的模組化設計
- 將功能獨立的程式碼封裝成函數,方便重複使用。
- 範例:計算圓的面積
float circle_area(float radius) { return 3.14159 * radius * radius; }
1.6 範例
- 範例 1:寫一個函數
square
,計算一個整數的平方。int square(int x) { return x * x; }
- 範例 2:寫一個函數
max
,比較兩個整數並回傳較大的值。int max(int a, int b) { if (a > b) { return a; } else { return b; } }
- 範例 3:寫一個函數
print_triangle
,根據輸入的高度用*
印出一個等腰直角三角形。void print_triangle(int height) { for (int i = 0; i < height; i++) { for (int j = 0; j < i; j++) { printf("*"); } printf("\n"); } }
1.7 綜合範例
以下是一個完整的程式範例,包含上述的 add
、square
、max
和 print_triangle
函數:
#include <stdio.h>
// 函數宣告
int add(int a, int b);
int square(int x);
int max(int a, int b);
void print_triangle(int height);
int main() {
// 呼叫 add 函數
int sum = add(3, 5);
printf("3 + 5 = %d\n", sum);
// 呼叫 square 函數
int squared = square(4);
printf("4 的平方是 %d\n", squared);
// 呼叫 max 函數
int larger = max(10, 7);
printf("10 和 7 中較大的數是 %d\n", larger);
// 呼叫 print_triangle 函數
printf("印出高度為 5 的直角三角形:\n");
print_triangle(5);
return 0;
}
// 函數定義
int add(int a, int b) {
return a + b;
}
int square(int x) {
return x * x;
}
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
void print_triangle(int height) {
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= i; j++) {
printf("*");
}
printf("\n");
}
}
2. 函數應用與遞迴
2.1 遞迴函數的基本概念
- 遞迴是什麼?
- 遞迴是一種函數呼叫自身的技術,通常用於解決可以分解為相同子問題的問題。
- 遞迴的兩個關鍵要素:
- 遞迴條件(Recursive Case):函數呼叫自身的條件。
- 終止條件(Base Case):停止遞迴的條件,避免無限遞迴。
- 遞迴的優點:
- 程式碼簡潔,容易理解。
- 適合解決具有遞迴性質的問題(如樹狀結構、分治法)。
- 遞迴的缺點:
- 可能導致堆疊溢出(Stack Overflow)。
- 效率較低,因為每次呼叫都會產生額外的函數呼叫開銷。
2.2 遞迴函數的語法
- 遞迴函數的定義:
回傳值類型 函數名稱(參數) { if (終止條件) { return 終止值; } else { return 函數名稱(修改後的參數); } }
- 範例:計算階乘的遞迴函數
int factorial(int n) { if (n == 1) { // 終止條件 return 1; } else { // 遞迴條件 return n * factorial(n - 1); } }
2.3 遞迴的執行過程
- 以
factorial(3)
為例:factorial(3) => 3 * factorial(2) => 3 * (2 * factorial(1)) => 3 * (2 * 1) => 3 * 2 => 6
2.4 遞迴與迴圈的比較
- 遞迴和迴圈都可以用來解決重複性問題,但各有優缺點。
- 範例:用迴圈實現階乘
int factorial_loop(int n) { int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; }
2.5 實作範例
- 範例 1:寫一個遞迴函數
fibonacci
,計算第 n 個費波那契數。 PS. F(N) = F(N-1) + F(N-2)int fibonacci(int n) { if (n <= 0) { // 終止條件 F(0) 或是負數 就 = 0 return 0; } else if (n == 1) { // 終止條件 return 1; } else { // 遞迴條件 return fibonacci(n - 1) + fibonacci(n - 2); } }
- 練習 2:寫一個遞迴函數
sum_digits
,計算一個整數的各位數字之和。int sum_digits(int n) { if (n < 10) { // 終止條件 return n; } else { // 遞迴條件 return n % 10 + sum_digits(n / 10); } }
2.6 綜合實作範例
以下是一個完整的程式範例,包含上述的 factorial
、fibonacci
和 sum_digits
函數:
#include <stdio.h>
// 函數宣告
int factorial(int n);
int fibonacci(int n);
int sum_digits(int n);
int main() {
// 呼叫 factorial 函數
int fact = factorial(5);
printf("5 的階乘是 %d\n", fact);
// 呼叫 fibonacci 函數
int fib = fibonacci(6);
printf("第 6 個費波那契數是 %d\n", fib);
// 呼叫 sum_digits 函數
int sum = sum_digits(1234);
printf("1234 的各位數字之和是 %d\n", sum);
return 0;
}
// 函數定義
int factorial(int n) {
if (n == 1) {
return 1;
}
else {
return n * factorial(n - 1);
}
}
int fibonacci(int n) {
if (n <= 0) {
return 0;
}
else if (n == 1) {
return 1;
}
else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
int sum_digits(int n) {
if (n < 10) {
return n;
}
else {
return n % 10 + sum_digits(n / 10);
}
}
3. 函數獨立封裝 API
**3.1 函數獨立封裝的概念
- 為什麼要封裝函數?
- 提高程式碼的模組化與可重用性。
- 隱藏實現細節,提供清晰的介面。
- 如何封裝函數?
- 將函數宣告放在標頭檔(
.h
檔案)。 - 將函數定義放在原始碼檔(
.c
檔案)。 - 在其他程式中包含標頭檔以使用這些函數。
- 將函數宣告放在標頭檔(
**3.2 標頭檔與原始碼檔的結構
- 標頭檔(
.h
檔案):- 包含函數宣告、常數定義、型別定義等。
- 範例:
geometry.h
#ifndef GEOMETRY_H #define GEOMETRY_H #define PI 3.14 // 計算圓的面積 float circle_area(float radius); // 計算矩形的面積 float rectangle_area(float width, float height); // 計算三角形的面積 float triangle_area(float base, float height); #endif
- 原始碼檔(
.c
檔案):- 包含函數的具體實現。
- 範例:
geometry.c
#include "geometry.h" // 計算圓的面積 float circle_area(float radius) { return PI * radius * radius; } // 計算矩形的面積 float rectangle_area(float width, float height) { return width * height; } // 計算三角形的面積 float triangle_area(float base, float height) { return 0.5 * base * height; }
**3.3 使用封裝的 API
- 在主程式中包含標頭檔,並呼叫封裝的函數。
- 範例:
main.c
#include <stdio.h> #include "geometry.h" int main() { // 計算圓的面積 float radius = 5.0; printf("半徑為 %.2f 的圓的面積是 %.2f\n", radius, circle_area(radius)); // 計算矩形的面積 float width = 4.0, height = 6.0; printf("寬為 %.2f、高為 %.2f 的矩形的面積是 %.2f\n", width, height, rectangle_area(width, height)); // 計算三角形的面積 float base = 3.0, triangle_height = 4.0; printf("底為 %.2f、高為 %.2f 的三角形的面積是 %.2f\n", base, triangle_height, triangle_area(base, triangle_height)); return 0; }
**3.4 使用 clang
編譯多檔案專案
- 編譯步驟:
- 編譯原始碼檔(
.c
檔案)為目標檔(.o
檔案)。 - 將目標檔連結成可執行檔。
- 編譯原始碼檔(
- 具體指令:
# 編譯 geometry.c 為目標檔 clang -c geometry.c -o geometry.o # 編譯 main.c 為目標檔 clang -c main.c -o main.o # 將目標檔連結成可執行檔 clang geometry.o main.o -o geometry_program # 執行可執行檔 ./geometry_program
- 輸出結果:
半徑為 5.00 的圓的面積是 78.54 寬為 4.00、高為 6.00 的矩形的面積是 24.00 底為 3.00、高為 4.00 的三角形的面積是 6.00
作業
作業目標
- 運用函數封裝的技巧,將幾何計算功能獨立成模組。
- 實作幾何相關的函數,並應用於具體問題。
- 培養學生解決實際問題的能力。
作業內容
目標:計算圓的周長與面積
- 請寫一個函數
circle_circumference
,計算圓的周長。 - 公式:
周長 = 2 * PI * 半徑
- 請寫一個函數
circle_area
,計算圓的面積。 - 公式:
周長 = PI * 半徑 * 半徑
- 請寫一個函數
目標:計算圓柱體積與表面積
- 利用目標 1 的2個函數來實現
目標:計算 1 + 2 ... + N 的總和
- 利用遞迴實現
作業要求
- 將所有幾何函數封裝在一個標頭檔
geometry.h
和一個原始碼檔geometry.c
中。 - 在主程式
main.c
中測試所有函數,並輸出結果。 - 程式碼需包含註解,說明每個函數的功能與輸入輸出。
範例輸出
請輸入圓柱的半徑: 10
請輸入圓柱的高度: 5
圓柱體積是: 1570
圓柱表面積是: 942
請輸入N: 10
1+2 ... +N = 55