函數的定義與使用
函數定義
類型標識符 函數名稱(形式參數表)
{
語句序列
}
<type1> name1 <type2> name2 ... <typen> namen
- 這些是被初始化的內部變數,其生命週期和可見性僅限於函數內部
- 表示返回值類型,由
return語句給出返回值 - 若無返回值,寫
void,不必寫return語句。
函數的呼叫
呼叫前先宣告函數:
- 若函數定義在呼叫點之前,可以不另外宣告;
- 若函數定義在呼叫點之後,必須要在呼叫函數前宣告函數原型:
類型標識符 被呼叫函數名稱(含類型說明的形參表);
- 呼叫形式
- 函數名稱(實參列表)
- 巢狀呼叫
- 在一個函數的函數體中,呼叫另一個函數。
- 遞迴呼叫
- 函數直接或間接呼叫自身。
例3-1:編寫一個求x的n次方的函數
#include <iostream>
using namespace std;
// 計算x的n次方
double power(double x, int n) {
double val = 1.0;
while (n--) val *= x;
return val;
}
int main() {
cout << "5 to the power 2 is " << power(5, 2) << endl;
return 0;
}
例3-2:數制轉換
題目:輸入一個8位二進位數,將其轉換為十進位數輸出。
例如:1101₂ = 1(2³) + 1(2²) + 0(2¹) + 1(2⁰) = 13₁₀
所以,如果輸入 1101,則應輸出 13。
#include <iostream>
using namespace std;
double power(double x, int n); // 計算x的n次方
int main() {
int value = 0;
cout << "請輸入一個8位二進位數字: ";
for (int i = 7; i >= 0; i--) {
char ch;
cin >> ch;
if (ch == '1')
value += static_cast<int>(power(2, i));
}
cout << "十進位值為 " << value << endl;
return 0;
}
double power(double x, int n) {
double val = 1.0;
while (n--)
val *= x;
return val;
}
例3-3:編寫程式求 π 的值
π 的計算公式如下:
其中 arctan 用如下形式的級數計算:
直到級數某項絕對值不大於 10⁻¹⁵ 為止;π 和 x 均為 double 型。
#include <iostream>
using namespace std;
double arctan(double x) {
double sqr = x * x;
double e = x;
double r = 0;
int i = 1;
while (e / i > 1e-15) {
double f = e / i;
r = (i % 4 == 1) ? r + f : r - f;
e = e * sqr;
i += 2;
}
return r;
}
int main() {
double a = 16.0 * arctan(1/5.0);
double b = 4.0 * arctan(1/239.0);
cout << "π = " << a - b << endl;
return 0;
}
例3-4:尋找並輸出11~999之間的數m,它滿足m、m²和m³均為回文數。
分析:用除以10取餘的方法,從最低位開始,依次取出該數的各位數字。按反序重新構成新的數,與原數比較是否相等,若相等,則原數為回文數。
#include <iostream>
using namespace std;
bool symm(unsigned n) {
unsigned i = n;
unsigned m = 0;
while (i > 0) {
m = m * 10 + i % 10;
i /= 10;
}
return m == n;
}
int main() {
for(unsigned m = 11; m < 1000; m++)
if (symm(m) && symm(m * m) && symm(m * m * m)) {
cout << "m = " << m;
cout << " m * m = " << m * m;
cout << " m * m * m = " << m * m * m << endl;
}
return 0;
}
例3-5:計算如下公式,並輸出結果:
其中 r、s 的值由鍵盤輸入。sin x 的近似值按如下公式計算,計算精度為 10⁻¹⁰。
#include <iostream>
#include <cmath>
using namespace std;
const double TINY_VALUE = 1e-10;
double tsin(double x) {
double g = 0;
double t = x;
int n = 1;
do {
g += t;
n++;
t = -t * x * x / (2 * n - 1) / (2 * n - 2);
} while (fabs(t) >= TINY_VALUE);
return g;
}
int main() {
double k, r, s;
cout << "r = ";
cin >> r;
cout << "s = ";
cin >> s;
if (r * r <= s * s)
k = sqrt(tsin(r)*tsin(r) + tsin(s)*tsin(s));
else
k = tsin(r * s) / 2;
cout << k << endl;
return 0;
}
例3-6:擲骰子的隨機遊戲
每個骰子有六面,點數分別為1、2、3、4、5、6。遊戲者在程式開始時輸入一個無符號整數,作為產生隨機數的種子。
每輪擲兩次骰子,第一輪如果和數為7或11則為勝,遊戲結束;和數為2、3或12則為負,遊戲結束;和數為其他值則將此值作為自己的點數,繼續第二輪、第三輪…直到某輪的和數等於點數則取勝,若在此前出現和數為7則為負。
由 rollDice 函數負責模擬擲骰子、計算和數並輸出和數。
#include <iostream>
#include <cstdlib>
using namespace std;
enum GameStatus { WIN, LOSE, PLAYING };
int main() {
int sum, myPoint;
GameStatus status;
unsigned seed;
int rollDice();
cout << "請輸入一個無符號整數: ";
cin >> seed; // 輸入隨機數種子
srand(seed); // 將種子傳遞給 rand()
sum = rollDice(); // 第一輪擲骰子、計算和數
switch (sum) {
case 7: // 如果和數為7或11則為勝狀態為WIN
case 11:
status = WIN;
break;
case 2: // 和數為2、3或12則為負狀態為LOSE
case 3:
case 12:
status = LOSE;
break;
default: // 其他情況,尚無結果,狀態為 PLAYING,記下點數
status = PLAYING;
myPoint = sum;
cout << "點數為 " << myPoint << endl;
break;
}
while (status == PLAYING) { // 只要狀態為PLAYING,繼續
sum = rollDice();
if (sum == myPoint) // 某輪的和數等於點數則取勝
status = WIN;
else if (sum == 7) // 出現和數為7則為負
status = LOSE;
}
if (status == WIN)
cout << "玩家勝利" << endl;
else
cout << "玩家失敗" << endl;
return 0;
}
int rollDice() {
int die1 = 1 + rand() % 6;
int die2 = 1 + rand() % 6;
int sum = die1 + die2;
cout << "玩家擲出 " << die1 << " + " << die2 << " = " << sum << endl;
return sum;
}
例3-7:輸入兩個整數,求平方和
#include <iostream>
using namespace std;
int fun2(int m) {
return m * m;
}
int fun1(int x, int y) {
return fun2(x) + fun2(y);
}
int main() {
int a, b;
cout << "請輸入兩個整數 (a 和 b): ";
cin >> a >> b;
cout << "a 和 b 的平方和: " << fun1(a, b) << endl;
return 0;
}
例3-8:求 n!
分析:計算 n! 的公式如下:
這是一個遞迴形式的公式,應該用遞迴函數實現。
#include <iostream>
using namespace std;
unsigned fac(int n) {
unsigned f;
if (n == 0)
f = 1;
else
f = fac(n - 1) * n;
return f;
}
int main() {
unsigned n;
cout << "請輸入一個正整數: ";
cin >> n;
unsigned y = fac(n);
cout << n << "! = " << y << endl;
return 0;
}
例3-9:用遞迴法計算從 n 個人中選擇 k 個人組成一個委員會的不同組合數。
分析:
由 n 個人裡選 k 個人的組合數 = 由 n-1 個人裡選 k 個人的組合數 + 由 n-1 個人裡選 k-1 個人的組合數。
當 n = k 或 k = 0 時,組合數為 1。
#include <iostream>
using namespace std;
int comm(int n, int k) {
if (k > n)
return 0;
else if (n == k || k == 0)
return 1;
else
return comm(n - 1, k) + comm(n - 1, k - 1);
}
int main() {
int n, k;
cout << "請輸入兩個整數 n 和 k: ";
cin >> n >> k;
cout << "C(n, k) = " << comm(n, k) << endl;
return 0;
}
例3-10:有三根針 A、B、C。A 針上有 N 個盤子,大的在下,小的在上,要求把這 N 個盤子從 A 針移到 C 針,在移動過程中可以借助 B 針,每次只允許移動一個盤,且在移動過程中在三根針上都保持大盤在下,小盤在上。
將 n 個盤子從 A 針移到 C 針可以分解為三個步驟:
- 將 A 上 n-1 個盤子移到 B 針上(借助 C 針);
- 把 A 針上剩下的一個盤子移到 C 針上;
- 將 n-1 個盤子從 B 針移到 C 針上(借助 A 針);
上面三個步驟包含兩種操作:
- 將多個盤子從一個針移到另一個針上,這是一個遞迴的過程。由
hanoi函數實現。 - 將1個盤子從一個針上移到另一針上。用
move函數實現。
#include <iostream>
using namespace std;
// 將 src 針的最上面一個盤子移動到 dest 針上
void move(char src, char dest) {
cout << src << " --> " << dest << endl;
}
// 將 n 個盤子從 src 針移動到 dest 針,以 medium 針作為中轉
void hanoi(int n, char src, char medium, char dest) {
if (n == 1)
move(src, dest);
else {
hanoi(n - 1, src, dest, medium);
move(src, dest);
hanoi(n - 1, medium, src, dest);
}
}
int main() {
int m;
cout << "請輸入盤子的數量: ";
cin >> m;
cout << m << " 個盤子的移動步驟如下:" << endl;
hanoi(m, 'A', 'B', 'C');
return 0;
}
函數的參數傳遞
- 在函數被呼叫時才分配形參的存儲單元
- 實參可以是常量、變數或表達式
- 實參類型必須與形參相符或可隱式轉換為形參類型
- 值傳遞是傳遞參數值,即單向傳遞
- 引用傳遞可以實現雙向傳遞
- 常引用作參數可以保障實參資料的安全
例3-11:輸入兩個整數並交換(值傳遞)
#include<iostream>
using namespace std;
void swap(int a, int b) {
int t = a;
a = b;
b = t;
}
int main() {
int x = 5, y = 10;
cout << "x = " << x << " y = " << y << endl;
swap(x, y);
cout << "x = " << x << " y = " << y << endl;
return 0;
}
例3-12:輸入兩個整數交換後輸出(引用傳遞)
#include<iostream>
using namespace std;
void swap(int& a, int& b) {
int t = a;
a = b;
b = t;
}
int main() {
int x = 5, y = 10;
cout << "x = " << x << " y = " << y << endl;
swap(x, y);
cout << "x = " << x << " y = " << y << endl;
return 0;
}
引用類型
引用 & 是標識符的別名。例如:
int i, j; int &ri = i; // 定義int引用ri,並初始化為變數i的引用 j = 10; ri = j; // 相當於 i = j;
可變數量形參
使用模板類 initializer_list<T>(T 為類模板,模板將於第9章介紹)可向函數傳遞同類型不定個數參數,例如:
void log_info(initializer_list<string> lst) {
for (auto &info: lst)
cout << info << ' ';
cout << endl;
}
log_info({"Hello", "world", "!"});
initializer_list<string> lst 接受不定個數字串實參
注意,實參以大括號列表方式給出
使用範圍 for 語句遍歷 lst,auto 自動推斷元素為 string 類型
