# 前言
為什麼選擇 C++ ,為什麼不選擇更常見的 Python ,在現代大語言模型讓程式碼產出的成本大幅度降低,我認為應該著重放在理解相關邏輯,應該在第一次學習的時候,盡量了解底層程式碼的思考邏輯,讓之後再學習相關的內容,有個好的基礎,當然並不是越底層越好,也不過度重複造輪子,我認為 C++ 是個很好的甜蜜點,在學習物件導向還有資料型態相關的邏輯,還有在 APCS 考試中與相關的演算法比賽 C++ 都是一定支援的,也有龐大的社群方便學習。
# C++ 語法
讓我們拆解以下程式碼,來更深入了解它:
範例
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << "Hello World!"; | |
return 0; | |
} |
# 範例解說
第 1 行: #include <iostream> 是一個標頭檔(header file)函式庫,讓我們可以使用輸入輸出物件,例如第 5 行使用的 cout 。標頭檔為 C++ 程式增加了功能。
第 2 行: using namespace std 代表我們可以直接使用標準函式庫(standard library)中物件與變數的名稱。
如果你還不太理解 #include <iostream> 和 using namespace std 是如何運作的,先不用擔心。只要把它們當作幾乎每個程式都會出現的固定寫法即可。
第 3 行:空白行。C++ 會忽略空白,但我們利用空白行讓程式碼更容易閱讀。
第 4 行:另一個幾乎每個 C++ 程式都會出現的東西是 int main() ,這稱為函式(function)。任何寫在其大括號 {} 內的程式碼都會被執行。
第 5 行: cout (唸作 "see-out")是一個物件,搭配插入運算子(insertion operator) << 來輸出/印出文字。在這個範例中,它會輸出 "Hello World!"。
注意:C++ 是區分大小寫的,"cout" 和 "Cout" 代表不同的意思。
注意:每一行 C++ 陳述式(statement)都必須以分號 ; 結尾。
注意: int main() 的內容也可以寫成這樣:
int main () { cout << "Hello World! "; return 0; } |
記住:編譯器會忽略空白字元。不過,將程式碼分成多行會讓它更容易閱讀。
第 6 行: return 0; 結束 main 函式。
第 7 行:別忘了加上結尾的大括號 } ,才能真正結束 main 函式。
# 省略命名空間(Omitting Namespace)
你可能會看到一些 C++ 程式在沒有 using namespace std; 這一行的情況下也能執行。 using namespace std; 這個陳述式是可以省略的,並且可以用 std 關鍵字加上 :: 運算子來取代,適用於部分物件(例如下方範例中的 std::cout ):
範例
#include <iostream> | |
int main() { | |
std::cout << "Hello World!"; | |
return 0; | |
} |
這兩種寫法在 C++ 中都是合法的。使用 std:: 可以清楚表明名稱的來源,並避免在較大型的程式中發生命名衝突。
# 我們會使用哪一種?
在這個教學中,我們大多會使用 using namespace std;
這樣可以讓程式碼更簡短、更容易閱讀,同時在學習 C++ 基礎時,能幫助初學者專注於理解語言本身,而不用在每個範例中反覆寫 std:: 。
當你開始撰寫更大型或更進階的程式時,可能會更偏好明確使用 std:: 。在實際的 C++ 程式碼中,你會看到這兩種寫法並存。
# C++ 陳述式(Statements)
電腦程式(computer program) 是一連串要讓電腦「執行(executed)」的「指令(instructions)」。
在程式語言中,這些程式指令被稱為 陳述式(statements)。
以下這行陳述式「指示」編譯器將文字 "Hello World" 印到螢幕上:
cout << "Hello World!"; |
務必記得以分號 ; 結束陳述式。
如果忘記加分號 ; ,就會發生錯誤,程式將無法執行:
cout << "Hello World!" |
# 多個陳述式
大多數 C++ 程式都包含許多陳述式。
這些陳述式會依照撰寫的順序,一個接一個地被執行:
cout << "Hello World!"; | |
cout << "Have a good day!"; | |
return 0; |
上面範例中共有三個陳述式:
cout << "Hello World!";cout << "Have a good day!";return 0;
第一個陳述式最先被執行(印出 "Hello World!");接著執行第二個陳述式(印出 "Have a good day!");最後執行第三個陳述式(成功結束 C++ 程式)。
只要記得永遠以分號結尾以避免錯誤即可。
# C++ 輸出(Output)
cout 物件搭配 << 運算子,用來輸出數值與印出文字。
記得將文字用雙引號( "" )包起來:
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << "Hello World!"; | |
return 0; | |
} |
你可以加入任意數量的 cout 物件。不過要注意,它並不會在輸出的結尾自動插入換行:
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << "Hello World!"; | |
cout << "I am learning C++"; | |
return 0; | |
} |
# 印出數字
cout 也可以用來印出數字。
不過和文字不同,數字不需要放在雙引號裡:
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << 3; | |
return 0; | |
} |
也可以直接進行數學運算:
cout << 3 + 3; |
cout << 2 * 5; |
# 換行(New Lines)
要在輸出中插入換行,可以使用 \n 字元:
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << "Hello World!\n"; | |
cout << "I am learning C++"; | |
return 0; | |
} |
也可以另外使用一個 << 運算子,把 \n 字元放在文字後面:
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << "Hello World!" << "\n"; | |
cout << "I am learning C++"; | |
return 0; | |
} |
小提示: 連續兩個 \n 字元會產生一個空白行:
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << "Hello World!" << "\n\n"; | |
cout << "I am learning C++"; | |
return 0; | |
} |
另一種插入換行的方式,是使用 endl 這個操縱器(manipulator):
#include <iostream> | |
using namespace std; | |
int main() { | |
cout << "Hello World!" << endl; | |
cout << "I am learning C++"; | |
return 0; | |
} |
\n 和 endl 都可以用來換行,不過 \n 較常被使用。
# \n 到底是什麼?
換行字元( \n )被稱為 跳脫序列(escape sequence),它會強制游標移動到螢幕上下一行的開頭,因此產生換行的效果。
其他常見的合法跳脫序列範例:
| 跳脫序列 | 說明 |
|---|---|
\t |
建立一個水平定位(tab) |
\\ |
插入反斜線字元(\) |
\" |
插入雙引號字元 |
# C++ 註解(Comments)
註解可以用來解釋 C++ 程式碼,讓程式更容易閱讀,也可以用來在測試替代程式碼時暫時停用某段程式。註解可以是單行或多行。
# 單行註解
單行註解以兩個斜線( // )開頭。
// 到該行結尾之間的任何文字都會被編譯器忽略(不會被執行)。
這個範例在一行程式碼前使用單行註解:
// This is a comment | |
cout << "Hello World!"; |
這個範例在一行程式碼的結尾使用單行註解:
cout << "Hello World!"; // This is a comment |
# C++ 多行註解
多行註解以 /* 開頭,並以 */ 結尾。
/* 和 */ 之間的任何文字都會被編譯器忽略:
/* The code below will print the words Hello World! | |
to the screen, and it is amazing */ | |
cout << "Hello World!"; |
# 該用單行還是多行註解?
這取決於你自己的選擇。一般來說,短註解會使用 // ,較長的說明則使用 /* */ 。
# C++ 變數(Variables)
變數是用來儲存資料值的容器。
C++ 中有多種不同 ** 型態(types)** 的變數(以不同關鍵字定義),例如:
int- 儲存整數(不含小數),例如 123 或 -123double- 儲存浮點數(含小數),例如 19.99 或 -19.99char- 儲存單一字元,例如 'a' 或 'B'。char 的值須用單引號包起來string- 儲存文字,例如 "Hello World"。string 的值須用雙引號包起來bool- 儲存兩種狀態的值:true 或 false
# 宣告(建立)變數
要建立變數,需指定型態並賦予它一個值:
語法:
type variableName = value; |
其中 type 是 C++ 型態之一(例如 int ), variableName 是變數的名稱(例如 x 或 myName)。等號用來將值賦予變數。
建立一個用來儲存數字的變數:
int myNum = 15; | |
cout << myNum; |
也可以先宣告變數而不賦值,稍後再賦值:
int myNum; | |
myNum = 15; | |
cout << myNum; |
# 改變變數的值
注意,如果對已存在的變數賦予新的值,會覆蓋掉先前的值:
int myNum = 15; // myNum is 15 | |
myNum = 10; // Now myNum is 10 | |
cout << myNum; // Outputs 10 |
# 其他型態
展示其他資料型態:
int myNum = 5; // Integer (whole number without decimals) | |
double myFloatNum = 5.99; // Floating point number (with decimals) | |
char myLetter = 'D'; // Character | |
string myText = "Hello"; // String (text) | |
bool myBoolean = true; // Boolean (true or false) |
# 顯示變數
cout 物件搭配 << 運算子用來顯示變數。
要同時結合文字與變數,用 << 運算子分隔它們:
int myAge = 35; | |
cout << "I am " << myAge << " years old."; |
也可以結合不同型態:
string name = "John"; | |
int age = 35; | |
double height = 6.1; | |
cout << name << " is " << age << " years old and " << height << " feet tall."; |
# 將變數相加
要把一個變數加到另一個變數,可以使用 + 運算子:
int x = 5; | |
int y = 6; | |
int sum = x + y; | |
cout << sum; |
# 宣告多個變數
要宣告多個相同型態的變數,可以用逗號分隔的清單:
int x = 5, y = 6, z = 50; | |
cout << x + y + z; |
也可以在同一行中,把相同的值賦予多個變數:
int x, y, z; | |
x = y = z = 50; | |
cout << x + y + z; |
# 識別字(Identifiers)
所有 C++ 變數都必須以唯一的名稱來識別。
這些唯一的名稱稱為識別字(identifiers)。
識別字可以是簡短的名稱(如 x 和 y),也可以是更具描述性的名稱(如 age、sum、totalVolume)。
注意: 建議使用具描述性的名稱,以建立容易理解與維護的程式碼:
// Good | |
int minutesPerHour = 60; | |
// OK, but not so easy to understand what m actually is | |
int m = 60; |
變數命名的一般規則:
- 名稱可以包含字母、數字與底線
- 名稱必須以字母或底線(_)開頭
- 名稱區分大小寫(
myVar與myvar是不同的變數) - 名稱不能包含空白或特殊字元,如!、#、% 等
- 保留字(如 C++ 關鍵字
int)不能作為名稱使用
# 常數(Constants)
當你不希望其他人(或自己)更動已存在的變數值時,可以使用 const 關鍵字(這會將變數宣告為「常數」,代表不可變更且唯讀):
const int myNum = 15; // myNum will always be 15 | |
myNum = 10; // error: assignment of read-only variable 'myNum' |
當你有不太可能改變的值時,應該一律將該變數宣告為常數:
const int minutesPerHour = 60; |
# 關於常數的注意事項
宣告常數變數時,必須同時賦予一個值:
const int minutesPerHour = 60; |
但以下寫法是行不通的:
const int minutesPerHour; | |
minutesPerHour = 60; // error |
# 實際案例
我們通常會把變數名稱簡化,以對應其資料型態(例如 int 型態用 myInt 或 myNum,char 型態用 myChar),這是為了避免混淆。
以下是一個實際使用變數的範例,程式儲存一位大學生的不同資料:
// Student data | |
int studentID = 15; | |
int studentAge = 23; | |
double studentFee = 75.25; | |
char studentGrade = 'B'; | |
// Print variables | |
cout << "Student ID: " << studentID << "\n"; | |
cout << "Student Age: " << studentAge << "\n"; | |
cout << "Student Fee: " << studentFee << "\n"; | |
cout << "Student Grade: " << studentGrade << "\n"; |
計算矩形面積的範例(將長與寬相乘):
// Create integer variables | |
int length = 4; | |
int width = 6; | |
// Calculate the area of a rectangle | |
int area = length * width; | |
// Print the variables | |
cout << "Length is: " << length << "\n"; | |
cout << "Width is: " << width << "\n"; | |
cout << "Area of the rectangle is: " << area << "\n"; |
# C++ 使用者輸入(User Input)
前面已經學過 cout 用來輸出(印出)數值。現在我們要使用 cin 來取得使用者輸入。
cin 是一個預先定義好的變數,會透過擷取運算子( >> )從鍵盤讀取資料。
以下範例中,使用者可以輸入一個數字,並儲存到變數 x ,接著印出 x 的值:
int x; | |
cout << "Type a number: "; // Type a number and press enter | |
cin >> x; // Get user input from the keyboard | |
cout << "Your number is: " << x; // Display the input value |
# 補充知識
cout 唸作 "see-out",用於輸出,使用插入運算子( << )
cin 唸作 "see-in",用於輸入,使用擷取運算子( >> )
# 建立一個簡易計算機
這個範例中,使用者需要輸入兩個數字,接著我們計算(相加)這兩個數字並印出總和:
int x, y; | |
int sum; | |
cout << "Type a number: "; | |
cin >> x; | |
cout << "Type another number: "; | |
cin >> y; | |
sum = x + y; | |
cout << "Sum is: " << sum; |
這樣你就完成了一個基本的計算機!
小提示: cin 與 cout 都屬於 <iostream> 函式庫,這是 standard input/output streams(標準輸入/輸出串流)的縮寫。
# C++ 資料型態(Data Types)
如同前面「變數」章節所提到的,C++ 中的變數必須指定資料型態:
int myNum = 5; // Integer (whole number) | |
float myFloatNum = 5.99; // Floating point number | |
double myDoubleNum = 9.98; // Floating point number | |
char myLetter = 'D'; // Character | |
bool myBoolean = true; // Boolean | |
string myText = "Hello"; // String |
# 基本資料型態
資料型態指定了變數要儲存的資訊的大小與種類:
| 資料型態 | 大小 | 說明 |
|---|---|---|
bool |
1 byte | 儲存 true 或 false 值 |
char |
1 byte | 儲存單一字元/字母/數字,或 ASCII 值 |
int |
2 或 4 bytes | 儲存整數,不含小數 |
float |
4 bytes | 儲存含小數的浮點數,精度約可儲存 6-7 位小數 |
double |
8 bytes | 儲存含小數的浮點數,精度約可儲存 15 位小數 |
# 數值型態(Numeric Types)
當你需要儲存不含小數的整數(例如 35 或 1000)時使用 int ;當你需要儲存含小數的浮點數(例如 9.99 或 3.14515)時,使用 float 或 double 。
int myNum = 1000; | |
cout << myNum; |
float myNum = 5.75; | |
cout << myNum; |
double myNum = 19.99; | |
cout << myNum; |
# float vs. double
浮點數值的精度代表該數值在小數點後能有多少位數。 float 的精度只有六到七位小數,而 double 變數的精度約為 15 位。因此,大多數計算情況下使用 double 較為安全。
# 科學記號數字
浮點數也可以是使用 "e" 表示 10 的次方的科學記號數字:
float f1 = 35e3; | |
double d1 = 12E4; | |
cout << f1; | |
cout << d1; |
# 布林資料型態(Boolean Data Types)
布林資料型態以 bool 關鍵字宣告,只能有 true 或 false 兩種值。
當這個值被回傳時, true = 1 , false = 0 。
bool isCodingFun = true; | |
bool isFishTasty = false; | |
cout << isCodingFun; // Outputs 1 (true) | |
cout << isFishTasty; // Outputs 0 (false) |
布林值大多用於條件判斷測試,這會在之後的章節詳細說明。
# 字元資料型態(Character Data Types)
char 資料型態用來儲存單一字元。字元必須用單引號包起來,例如 'A' 或 'c':
char myGrade = 'B'; | |
cout << myGrade; |
如果你熟悉 ASCII,也可以使用 ASCII 值來顯示特定字元:
char a = 65, b = 66, c = 67; | |
cout << a; | |
cout << b; | |
cout << c; |
# 字串資料型態(String Data Types)
string 型態用來儲存一連串的字元(文字)。這不是 C++ 的內建型態,但在最基本的用法上它的行為就像內建型態一樣。字串的值必須用雙引號包起來:
string greeting = "Hello"; | |
cout << greeting; |
要使用字串,必須在原始碼中額外引入 <string> 標頭檔函式庫:
// Include the string library | |
#include <string> | |
// Create a string variable | |
string greeting = "Hello"; | |
// Output string value | |
cout << greeting; |
# auto 關鍵字
auto 關鍵字會根據你賦予變數的值,自動判斷該變數的型態。
它能幫助你寫出更簡潔的程式碼,並避免重複撰寫型態,尤其是在型態較長或較複雜時特別有用。
舉例來說: 與其寫 int x = 5; ,你可以寫成:
auto x = 5; // x is automatically treated as int |
從 C++11 開始, auto 成為讓編譯器根據所賦予的值自動判斷型態的強大工具。
以下範例展示 auto 如何根據賦予的值,建立不同型態的變數:
// Creating auto variables | |
auto myNum = 5; // int | |
auto myFloatNum = 5.99f; // float | |
auto myDoubleNum = 9.98; // double | |
auto myLetter = 'D'; // char | |
auto myBoolean = true; // bool | |
auto myString = string("Hello"); // std::string |
# 重要注意事項
auto只有在同時賦值的情況下才能運作(不能只宣告auto x;而不賦值)- 一旦型態被決定,就會固定不變,如以下範例:
auto x = 5; // x is now an int | |
x = 10; // OK - still an int | |
x = 9.99; // Error - can't assign a double to an int |
注意: 在這份教學中,當型態簡單且容易辨識時,我們通常會使用 int 、 double 等基本型態。
但對於較複雜的型態,例如之後章節會學到的疊代器(iterators)與 lambda 運算式,我們會使用 auto 讓程式碼更簡潔、更容易理解。
# 實際案例
以下是使用不同資料型態的實際案例,計算並印出多項商品的總花費:
// Create variables of different data types | |
int items = 50; | |
double cost_per_item = 9.99; | |
double total_cost = items * cost_per_item; | |
char currency = '$'; | |
// Print variables | |
cout << "Number of items: " << items << "\n"; | |
cout << "Cost per item: " << cost_per_item << currency << "\n"; | |
cout << "Total cost = " << total_cost << currency << "\n"; |
# C++ 運算子(Operators)
運算子用來對變數與數值執行運算。
以下範例中,我們使用 + 運算子將兩個值相加:
int x = 100 + 50; |
雖然 + 運算子最常用於將兩個值相加(如上面的範例),但它也可以用於將變數與數值相加,或是將變數與另一個變數相加:
int sum1 = 100 + 50; // 150 (100 + 50) | |
int sum2 = sum1 + 250; // 400 (150 + 250) | |
int sum3 = sum2 + sum2; // 800 (400 + 400) |
C++ 將運算子分為以下幾類:
- 算術運算子(Arithmetic operators)
- 賦值運算子(Assignment operators)
- 比較運算子(Comparison operators)
- 邏輯運算子(Logical operators)
- 位元運算子(Bitwise operators)
# 算術運算子(Arithmetic Operators)
算術運算子用來執行常見的數學運算。
| 運算子 | 名稱 | 說明 | 範例 |
|---|---|---|---|
+ |
加法 | 將兩個值相加 | x + y |
- |
減法 | 從一個值減去另一個值 | x - y |
* |
乘法 | 將兩個值相乘 | x * y |
/ |
除法 | 將一個值除以另一個值 | x / y |
% |
取餘數 | 回傳除法的餘數 | x % y |
++ |
遞增 | 將變數的值加 1 | ++x |
-- |
遞減 | 將變數的值減 1 | --x |
以下範例在同一個範例中使用了不同的算術運算子:
int x = 10; | |
int y = 3; | |
cout << (x + y) << "\n"; // 13 | |
cout << (x - y) << "\n"; // 7 | |
cout << (x * y) << "\n"; // 30 | |
cout << (x / y) << "\n"; // 3 (integer division) | |
cout << (x % y) << "\n"; // 1 | |
int z = 5; | |
++z; | |
cout << z << "\n"; // 6 | |
--z; | |
cout << z << "\n"; // 5 |
注意: 在 C++ 中將兩個整數相除時,結果也會是整數。例如 10 / 3 會得到 3 。如果想要有小數的結果,需使用 float 或 double 值,例如 10.0 / 3 。
int x = 10; | |
int y = 3; | |
cout << (x / y) << "\n"; // Integer division, result is 3 | |
double a = 10.0; | |
double b = 3.0; | |
cout << (a / b) << "\n"; // Decimal division, result is 3.333... |
# 遞增與遞減
遞增與遞減在程式設計中非常常見,尤其是在使用計數器、迴圈與陣列時(會在之後的章節詳細說明)。
++ 運算子將值加 1, -- 運算子將值減 1:
int x = 5; | |
++x; // Increment x by 1 | |
cout << x << "\n"; // 6 |
int x = 5; | |
--x; // Decrement x by 1 | |
cout << x << "\n"; // 4 |
有時候你可能會同時對同一個變數做遞增與遞減。要記得,如果先增加一個值、之後再減少它,結果會先加 1 再減 1—— 最後會回到原本的值:
int x = 5; | |
++x; // Increment x by 1 (x becomes 6) | |
--x; // Decrement x by 1 (x becomes 5 again) | |
cout << x << "\n"; // 5 |
# 實際案例:計算人數
假設你正在寫一個程式,用來計算有多少人進出一個房間。可以用 ++ 在有人進來時增加計數器,用 -- 在有人離開時減少計數器:
int peopleInRoom = 0; | |
// 3 people enter | |
peopleInRoom++; | |
peopleInRoom++; | |
peopleInRoom++; | |
cout << peopleInRoom << "\n"; // 3 | |
// 1 person leaves | |
peopleInRoom--; | |
cout << peopleInRoom << "\n"; // 2 |
# 賦值運算子(Assignment Operators)
賦值運算子用來將值賦予變數。
以下範例中,我們使用賦值運算子( = )將值 10 賦予名為 x 的變數:
int x = 10; |
加法複合賦值運算子( += )會將一個值加到變數上:
int x = 10; | |
x += 5; // same as x = x + 5 |
所有賦值運算子清單:
| 運算子 | 範例 | 相當於 |
|---|---|---|
= |
x = 5 | x = 5 |
+= |
x += 3 | x = x + 3 |
-= |
x -= 3 | x = x - 3 |
*= |
x *= 3 | x = x * 3 |
/= |
x /= 3 | x = x / 3 |
%= |
x %= 3 | x = x % 3 |
&= |
x &= 3 | x = x & 3 |
\|= |
x |= 3 | x = x | 3 |
^= |
x ^= 3 | x = x ^ 3 |
>>= |
x >>= 3 | x = x >> 3 |
<<= |
x <<= 3 | x = x << 3 |
# 複合賦值運算子
複合賦值運算子是一種較簡短的寫法,用於在賦值兩側都使用同一個變數的運算。例如,不用寫 x = x + 5; ,可以直接寫 x += 5; 。
int x = 10; | |
x += 5; // same as x = x + 5 | |
cout << x << "\n"; // 15 | |
x *= 2; // same as x = x * 2 | |
cout << x << "\n"; // 30 |
小提示: 複合運算子讓程式碼更短、更易讀,尤其是在多次更新同一個變數時特別有用。
為什麼叫做「複合(Compound)」?
之所以稱為 複合賦值運算子,是因為它們把一般運算子(如 + 、 - 、 * 等)與賦值運算子( = )結合成一個單一運算子。例如, += 就是 + 與 = 的組合。
# 實際案例:追蹤存款
複合賦值運算子也可以用於實際情境。例如,可以用 += 運算子在存錢到帳戶時追蹤存款金額:
int savings = 100; | |
savings += 50; // add 50 to savings | |
cout << "Total savings: " << savings; |
# 比較運算子(Comparison Operators)
比較運算子用來比較兩個值(或變數)。這在程式設計中很重要,因為它能幫助我們找出答案並做出判斷。
比較運算的回傳值是 1 或 0 ,代表 true(1)或 false(0)。這些值被稱為布林值(Boolean values),會在之後的 Booleans 與 If...Else 章節詳細說明。
以下範例中,我們使用大於運算子( > )來判斷 5 是否大於 3:
int x = 5; | |
int y = 3; | |
cout << (x > y); // returns 1 (true) because 5 is greater than 3 |
所有比較運算子清單:
| 運算子 | 名稱 | 範例 |
|---|---|---|
== |
等於 | x == y |
!= |
不等於 | x != y |
> |
大於 | x > y |
< |
小於 | x < y |
>= |
大於等於 | x >= y |
<= |
小於等於 | x <= y |
# 實際案例
比較運算子經常用於實際情境的條件判斷,例如檢查一個人的年齡是否足以投票:
int age = 18; | |
cout << (age >= 18) << "\n"; // 1 (true), old enough to vote | |
cout << (age < 18) << "\n"; // 0 (false) |
另一個常見的用法是檢查密碼長度是否足夠:
int passwordLength = 5; | |
cout << (passwordLength >= 8) << "\n"; // 0 (false), too short | |
cout << (passwordLength < 8) << "\n"; // 1 (true), needs more characters |
# 邏輯運算子(Logical Operators)
如同比較運算子一樣,你也可以使用邏輯運算子來測試 true( 1 )或 false( 0 )的值。
邏輯運算子用來判斷變數或值之間的邏輯關係:
| 運算子 | 名稱 | 說明 | 範例 |
|---|---|---|---|
&& |
邏輯 and | 兩個陳述式都為真時回傳 true | x < 5 && x < 10 |
\|\| |
邏輯 or | 其中一個陳述式為真時回傳 true | x < 5 || x < 4 |
! |
邏輯 not | 反轉結果,若結果為真則回傳 false | !(x < 5 && x < 10) |
之後的章節會更詳細說明 true 與 false 值。
# 運算子優先順序(Operator Precedence)
當一個運算式中包含超過一個運算子時,C++ 會依照 ** 運算順序(order of operations)** 規則來決定先計算哪個部分。
例如,乘法會先於加法計算:
int result1 = 2 + 3 * 4; // 2 + 12 = 14 | |
int result2 = (2 + 3) * 4; // 5 * 4 = 20 | |
cout << result1 << "\n"; | |
cout << result2 << "\n"; |
# 為什麼會這樣?
在 2 + 3 * 4 中,乘法會先被計算,所以答案是 14 。
如果想讓加法先被計算,必須使用括號: (2 + 3) * 4 ,結果會是 20 。
小提示: 如果想確保運算按照你預期的順序進行,務必使用括號 ( ) 。這也能讓程式碼更容易閱讀。
# 運算順序
以下是 C++ 中一些常見運算子,依優先順序由高到低排列:
()- 括號*、/、%- 乘法、除法、取餘數+、-- 加法、減法>、<、>=、<=- 比較==、!=- 相等判斷&&- 邏輯 AND||- 邏輯 OR=- 賦值
# 另一個範例
減法與加法會由左至右依序計算,除非加上括號:
int result1 = 10 - 2 + 5; // (10 - 2) + 5 = 13 | |
int result2 = 10 - (2 + 5); // 10 - 7 = 3 | |
cout << result1 << "\n"; | |
cout << result2 << "\n"; |
記住: 括號永遠優先運算。使用括號來控制計算的順序。
# C++ 字串(Strings)
字串用來儲存文字/字元。
例如,"Hello World" 就是一個字串。
string 變數包含一連串用雙引號( "" )包起來的字元:
建立一個 string 型態的變數並賦值:
string greeting = "Hello"; |
要使用字串,必須在原始碼中額外引入 <string> 標頭檔函式庫:
// Include the string library | |
#include <string> | |
// Create a string variable | |
string greeting = "Hello"; | |
// Print the string | |
cout << greeting; |
注意: 字串可以包含多個單字、空格與標點符號:
string greeting = "Hello and welcome!"; | |
cout << greeting; |
# 字串串接(String Concatenation)
+ 運算子可以用在字串之間,將它們相加組成新的字串,這稱為串接(concatenation):
string firstName = "John "; | |
string lastName = "Doe"; | |
string fullName = firstName + lastName; | |
cout << fullName; |
上面的範例中,我們在 firstName 後面加了一個空格,讓輸出時 John 與 Doe 之間有空格。不過,也可以用引號另外加上空格( " " 或 ' ' ):
string firstName = "John"; | |
string lastName = "Doe"; | |
string fullName = firstName + " " + lastName; | |
cout << fullName; |
# Append
C++ 中的字串其實是一個物件,內含可以對字串執行特定操作的函式。例如,也可以用 append() 函式來串接字串:
string firstName = "John "; | |
string lastName = "Doe"; | |
string fullName = firstName.append(lastName); | |
cout << fullName; |
# 數字與字串相加
警告!
C++ 對 + 運算子同時用於加法與串接。
數字會被相加,字串會被串接。
如果將兩個數字相加,結果會是一個數字:
int x = 10; | |
int y = 20; | |
int z = x + y; // z will be 30 (an integer) |
如果將兩個字串相加,結果會是字串串接:
string x = "10"; | |
string y = "20"; | |
string z = x + y; // z will be 1020 (a string) |
如果嘗試將數字加到字串上,會發生錯誤:
string x = "10"; | |
int y = 20; | |
string z = x + y; |
# 字串長度(String Length)
要取得字串的長度,可以使用 length() 函式:
string txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
cout << "The length of the txt string is: " << txt.length(); |
小提示: 有些 C++ 程式會使用 size() 函式來取得字串長度,這只是 length() 的別名。你可以自行選擇要用 length() 還是 size() :
string txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
cout << "The length of the txt string is: " << txt.size(); |
# 存取字串(Access Strings)
可以透過方括號 [] 內的索引值來存取字串中的字元。
這個範例印出 myString 中的第一個字元:
string myString = "Hello"; | |
cout << myString[0]; | |
// Outputs H |
注意: 字串索引從 0 開始:[0] 是第一個字元,[1] 是第二個字元,以此類推。
這個範例印出 myString 中的第二個字元:
string myString = "Hello"; | |
cout << myString[1]; | |
// Outputs e |
要印出字串的最後一個字元,可以使用以下程式碼:
string myString = "Hello"; | |
cout << myString[myString.length() - 1]; | |
// Outputs o |
# 修改字串字元
要修改字串中特定字元的值,可以透過索引值並使用單引號:
string myString = "Hello"; | |
myString[0] = 'J'; | |
cout << myString; | |
// Outputs Jello instead of Hello |
# at() 函式
<string> 函式庫也有一個 at() 函式,可以用來存取字串中的字元:
string myString = "Hello"; | |
cout << myString; // Outputs Hello | |
cout << myString.at(0); // First character | |
cout << myString.at(1); // Second character | |
cout << myString.at(myString.length() - 1); // Last character | |
myString.at(0) = 'J'; | |
cout << myString; // Outputs Jello |
# 特殊字元(Special Characters)
因為字串必須寫在引號內,以下這種字串寫法會讓 C++ 誤解,並產生錯誤:
string txt = "We are the so-called "Vikings" from the north."; |
要避免這個問題,可以使用反斜線跳脫字元。
反斜線( \ )跳脫字元會把特殊字元轉換成字串字元:
| 跳脫字元 | 結果 | 說明 |
|---|---|---|
\' |
' | 單引號 |
\" |
" | 雙引號 |
\\ |
\ | 反斜線 |
\" 序列會在字串中插入雙引號:
string txt = "We are the so-called \"Vikings\" from the north."; |
\' 序列會在字串中插入單引號:
string txt = "It\'s alright."; |
\\ 序列會在字串中插入一個反斜線:
string txt = "The character \\ is called backslash."; |
其他常見的 C++ 跳脫字元:
| 跳脫字元 | 結果 |
|---|---|
\n |
換行 |
\t |
Tab |
# 使用者輸入字串(User Input Strings)
可以在 cin 上使用擷取運算子 >> 來儲存使用者輸入的字串:
string firstName; | |
cout << "Type your first name: "; | |
cin >> firstName; // get user input from the keyboard | |
cout << "Your name is: " << firstName; | |
// Type your first name: John | |
// Your name is: John |
不過, cin 會把空格(空白、tab 等)視為終止字元,這代表它只能儲存單一個單字(即使輸入了多個單字):
string fullName; | |
cout << "Type your full name: "; | |
cin >> fullName; | |
cout << "Your name is: " << fullName; | |
// Type your full name: John Doe | |
// Your name is: John |
從上面的範例可以看到,雖然預期程式會印出 "John Doe",但實際上只印出了 "John"。
因此,在處理字串時,我們經常使用 getline() 函式來讀取一整行文字。它的第一個參數是 cin ,第二個參數是字串變數:
string fullName; | |
cout << "Type your full name: "; | |
getline (cin, fullName); | |
cout << "Your name is: " << fullName; | |
// Type your full name: John Doe | |
// Your name is: John Doe |
# 省略命名空間
有些 C++ 程式會在沒有標準命名空間函式庫的情況下執行。 using namespace std 這一行可以省略,改為在 string (以及 cout )物件前加上 std 關鍵字與 :: 運算子:
#include <iostream> | |
#include <string> | |
// using namespace std; - Remove this line | |
int main() { | |
std::string greeting = "Hello"; | |
std::cout << greeting; | |
return 0; | |
} |
是否要引入標準命名空間函式庫,取決於你自己的選擇。在這份教學中,我們會繼續引入該函式庫。
# C 風格字串(C-Style Strings)
C 風格字串是用 char 型態建立的,而不是 string 。
這個名稱來自 C 語言,因為 C 語言不像許多其他程式語言擁有可以輕鬆建立字串變數的 string 型態。取而代之,必須使用 char 型態並建立一個字元陣列(array)來組成 C 語言中的「字串」。
由於 C++ 是作為 C 語言的延伸而開發的,因此它延續了這種在 C++ 中建立字串的方式:
string greeting1 = "Hello"; // Regular String | |
char greeting2[] = "Hello"; // C-Style String (an array of characters) |
注意: 使用標準的 string 型態會比使用 C 風格字串更方便。不過,有些使用者仍然使用 C 風格字串的其中一個原因,是因為它們可以使用 C 標準函式庫中的函式。
# C++ 數學(Math)
C++ 提供許多函式,讓你可以對數字執行數學運算。
# 最大值與最小值
max(x, y) 函式可以用來找出 x 與 y 中較大的值:
cout << max(5, 10); |
min(x, y) 函式可以用來找出 x 與 y 中較小的值:
cout << min(5, 10); |
# C++ <cmath> 函式庫
其他函式,例如 sqrt (平方根)、 round (四捨五入)與 log (自然對數),可以在 <cmath> 標頭檔中找到:
// Include the cmath library | |
#include <cmath> | |
cout << sqrt(64); | |
cout << round(2.6); | |
cout << log(2); |
# C++ 布林值(Booleans)
在程式設計中,你經常會需要一種只能有兩種值之一的資料型態,例如:
- YES / NO
- ON / OFF
- TRUE / FALSE
為此,C++ 提供了 bool 資料型態,其值可以是 true (1)或 false (0)。
# 布林值
布林變數以 bool 關鍵字宣告,其值可以是 true 或 false :
bool isCodingFun = true; | |
bool isFishTasty = false; | |
cout << isCodingFun << "\n"; // Outputs 1 (true) | |
cout << isFishTasty << "\n"; // Outputs 0 (false) |
從上面的範例可以看出, true 值會回傳 1 , false 值會回傳 0 。
# 使用 boolalpha 印出 true/false
如果你希望印出 true 與 false 這樣的文字,而不是 1 與 0 ,可以使用 boolalpha 操縱器:
bool isCodingFun = true; | |
bool isFishTasty = false; | |
cout << boolalpha; // enable printing "true"/"false" | |
cout << isCodingFun << "\n"; // Outputs true | |
cout << isFishTasty << "\n"; // Outputs false |
注意: boolalpha 不是一種資料型態,而是一種 I/O 操縱器(I/O manipulator)—— 用來改變 cout 顯示布林值方式的設定。當你使用它時,等於是在告訴 cout :「從現在開始,將布林值印成 true 與 false ,而不是 1 與 0 。」
# 用 noboolalpha 重設回預設值
如果想回到預設行為(印出 1 與 0 ),可以使用 noboolalpha :
bool isCodingFun = true; | |
cout << boolalpha; // print as true/false | |
cout << isCodingFun << "\n"; // Outputs true | |
cout << noboolalpha; // reset to 1/0 | |
cout << isCodingFun << "\n"; // Outputs 1 |
注意: 要用預設的 1 與 0 ,還是用文字 true 與 false ,取決於你自己的偏好。兩者在 C++ 中都是正確的寫法,且可以透過 boolalpha 與 noboolalpha 互相切換。
上面的範例都是使用固定的布林值。但在實際的程式中,布林值通常是比較數值或變數之後得到的結果,這會在下一節詳細說明。
# 布林運算式(Boolean Expressions)
布林運算式(Boolean expression) 是一段用來比較數值或變數,並回傳布林值 1 (true)或 0 (false)的程式碼。
布林運算式是程式設計中做決策的基礎 —— 它們讓程式能根據某件事是真是假來決定要做什麼。
可以使用比較運算子,例如大於( > )運算子,來判斷一個運算式(或變數)是真還是假:
int x = 10; | |
int y = 9; | |
cout << (x > y); // returns 1 (true), because 10 is higher than 9 |
或是更簡單地:
cout << (10 > 9); // returns 1 (true), because 10 is higher than 9 |
以下範例使用等於( == )運算子來計算運算式的結果:
int x = 10; | |
cout << (x == 10); // returns 1 (true), because the value of x is equal to 10 |
cout << (10 == 15); // returns 0 (false), because 10 is not equal to 15 |
# 將結果存進布林變數
也可以把比較的結果存進一個 bool 變數:
將 x > y 的比較結果存進布林變數,並印出它:
int x = 10; | |
int y = 9; | |
bool isGreater = x > y; | |
cout << isGreater; // returns 1 (true) |
注意: 是否要把比較結果存成布林變數,或是直接使用比較運算,取決於你自己。將結果存起來可以讓程式碼更容易閱讀,尤其是想要重複使用該結果時。
# 實際案例
讓我們用一個實際案例來使用布林值,判斷一個人的年齡是否足以投票。
以下範例中,我們使用 >= 比較運算子,判斷年齡( 25 )是否大於或等於投票年齡限制 18 :
int myAge = 25; | |
int votingAge = 18; | |
cout << (myAge >= votingAge); // returns 1 (true), meaning 25 year olds are allowed to vote! |
更好的做法(既然我們已經上手了),是把上面的程式碼包進 if...else 陳述式中,這樣就能依照結果執行不同的動作:
如果 myAge 大於或等於 18 ,就輸出 "Old enough to vote!",否則輸出 "Not old enough to vote.":
int myAge = 25; | |
int votingAge = 18; | |
if (myAge >= votingAge) { | |
cout << "Old enough to vote!"; | |
} else { | |
cout << "Not old enough to vote."; | |
} | |
// Outputs: Old enough to vote! |
布林值是所有 C++ 比較與條件判斷的基礎。之後的章節會詳細說明條件判斷( if...else )。
# C++ If...Else
前面已經知道 C++ 支援數學上熟悉的比較條件,例如:
- 小於:a < b
- 小於等於:a <= b
- 大於:a > b
- 大於等於:a >= b
- 等於:a == b
- 不等於:a != b
這些條件用來根據某件事是真或假,來執行不同的動作。
C++ 有以下幾種條件陳述式:
- 使用
if指定一段程式碼區塊,在條件為真時執行 - 使用
else指定一段程式碼區塊,在同一個條件為假時執行 - 使用
else if指定一個新的條件,在第一個條件為假時測試 - 使用
switch指定多個可供選擇的程式碼區塊
# if 陳述式
使用 if 陳述式來指定一段 C++ 程式碼區塊,在條件為 true 時執行。
語法:
if (condition) { | |
// block of code to be executed if the condition is true | |
} |
注意 if 必須以小寫字母書寫,若寫成大寫( If 或 IF )會產生錯誤。
以下範例測試兩個值,判斷 20 是否大於 18。如果條件為 true ,就印出訊息:
if (20 > 18) { | |
cout << "20 is greater than 18"; | |
} |
也可以在條件中使用變數:
int x = 20; | |
int y = 18; | |
if (x > y) { | |
cout << "x is greater than y"; | |
} |
上面的範例中,我們使用兩個變數 x 與 y,測試 x 是否大於 y。因為 20 大於 18,條件為真,所以印出了訊息。
# 使用布林變數
由於 if 陳述式的條件必須是 true 或 false,你可以將結果存進一個布林變數,而不用直接寫比較運算:
int x = 20; | |
int y = 18; | |
bool isGreater = x > y; | |
if (isGreater) { | |
cout << "x is greater than y"; | |
} |
這樣可以讓程式碼更容易閱讀,尤其是條件較複雜或會重複使用時。
# else 陳述式
使用 else 陳述式來指定一段程式碼區塊,在條件為 false 時執行。
語法:
if (condition) { | |
// block of code to be executed if the condition is true | |
} else { | |
// block of code to be executed if the condition is false | |
} |
以下範例中,程式檢查 time 的值。如果小於 18,就印出 "Good day.";否則印出 "Good evening.":
int time = 20; | |
if (time < 18) { | |
cout << "Good day."; | |
} else { | |
cout << "Good evening."; | |
} | |
// Outputs "Good evening." |
因為 time 是 20,所以條件 time < 18 為假,因此執行了 else 區塊內的程式碼,印出 "Good evening."。如果 time 小於 18,程式就會印出 "Good day."。
# 使用布林變數
也可以把條件存進布林變數,並在 if...else 陳述式中使用它,這樣能讓程式碼更容易閱讀:
int time = 20; | |
bool isDay = time < 18; | |
if (isDay) { | |
cout << "Good day."; | |
} else { | |
cout << "Good evening."; | |
} | |
// Outputs "Good evening." |
小提示: 像 isDay 這樣的名稱,能讓人容易理解這個條件代表的意思。
# else if 陳述式
使用 else if 陳述式,在第一個條件為 false 時,指定一個新的條件進行測試。
可以使用 else if 依序檢查多個條件。
語法:
if (condition1) { | |
// block of code to be executed if condition1 is true | |
} else if (condition2) { | |
// block of code to be executed if condition1 is false and condition2 is true | |
} else { | |
// block of code to be executed if both conditions are false | |
} |
這些條件會由上到下依序檢查。一旦某個條件為 true ,就會執行對應的程式碼區塊,其餘的條件則會被跳過。
# 範例
以下範例依照 time 的值,決定要印出哪一則訊息:
int time = 16; | |
if (time < 12) { | |
cout << "Good morning."; | |
} else if (time < 18) { | |
cout << "Good day."; | |
} else { | |
cout << "Good evening."; | |
} | |
// Outputs "Good day." |
time 的值是 16。第一個條件( time < 12 )為假,但第二個條件( time < 18 )為真。因此執行了 else if 區塊中的程式碼,印出 "Good day."。
如果 time 的值是 22,所有條件都會是假,程式就會印出 "Good evening."。
# 使用布林變數
與 if 陳述式一樣,也可以把條件存進布林變數,並搭配 else if 使用:
int time = 16; | |
bool isMorning = time < 12; | |
bool isDay = time < 18; | |
if (isMorning) { | |
cout << "Good morning."; | |
} else if (isDay) { | |
cout << "Good day."; | |
} else { | |
cout << "Good evening."; | |
} |
小提示: 像 isMorning 與 isDay 這樣的布林變數名稱,能讓人更容易理解每個條件代表的意思。
# 簡寫 If...Else(三元運算子)
還有一種 if...else 的簡寫形式,稱為三元運算子(ternary operator),因為它使用了三個運算元。
三元運算子會根據條件_回傳一個值_:如果條件為 true ,就回傳第一個值;否則回傳第二個值。
它可以用來將多行程式碼簡化成一行,通常用來取代簡單的 if...else 陳述式:
語法:
variable = (condition) ? expressionTrue : expressionFalse; |
與其寫成:
int time = 20; | |
if (time < 18) { | |
cout << "Good day."; | |
} else { | |
cout << "Good evening."; | |
} |
可以直接簡化成:
int time = 20; | |
string result = (time < 18) ? "Good day." : "Good evening."; | |
cout << result; |
也可以直接在 cout 陳述式中使用三元運算子:
int time = 20; | |
cout << ((time < 18) ? "Good day." : "Good evening."); |
小提示: 三元運算子適合用在簡短、簡單的條件。若邏輯較長或複雜,一般的 if...else 陳述式會更容易閱讀。
# 巢狀三元運算子
可以巢狀使用三元運算子來處理兩種以上的結果,但這會讓程式碼較難閱讀:
int time = 22; | |
string message = (time < 12) ? "Good morning." | |
: (time < 18) ? "Good afternoon." | |
: "Good evening."; | |
cout << message; |
注意: 雖然巢狀三元運算子可以運作,但為了清楚易讀,通常建議使用一般的 if...else if...else 陳述式。
# 巢狀 if(Nested If)
也可以把一個 if 陳述式放進另一個 if 裡面,這稱為 ** 巢狀 if(nested if)** 陳述式。
巢狀 if 讓你只有在另一個條件已經為 true 時,才去檢查某個條件。
語法:
if (condition1) { | |
// code to run if condition1 is true | |
if (condition2) { | |
// code to run if both condition1 and condition2 are true | |
} | |
} |
# 範例
這個範例中,我們先檢查 x 是否大於 10,如果是,才接著檢查 y 是否大於 20:
int x = 15; | |
int y = 25; | |
if (x > 10) { | |
cout << "x is greater than 10\n"; | |
// Nested if | |
if (y > 20) { | |
cout << "y is also greater than 20\n"; | |
} | |
} |
# 實際案例
巢狀 if 陳述式適合用在需要測試多個彼此相依的條件時。例如,檢查一個人的年齡是否足以投票,並且是否為公民:
int age = 20; | |
bool isCitizen = true; | |
if (age >= 18) { | |
cout << "Old enough to vote.\n"; | |
if (isCitizen) { | |
cout << "And you are a citizen, so you can vote!\n"; | |
} else { | |
cout << "But you must be a citizen to vote.\n"; | |
} | |
} else { | |
cout << "Not old enough to vote.\n"; | |
} |
# 注意事項
- 可以巢狀使用任意數量的
if陳述式,但要避免巢狀層數太深,否則會變得難以閱讀。 - 巢狀
if經常與else和else if搭配使用,以進行更複雜的決策判斷。
# 條件判斷中的邏輯運算子
可以使用邏輯運算子來組合或反轉條件,這些運算子可以與 if 、 else 、 else if 一起使用,建立更複雜的判斷。
&&(AND)—— 所有條件都必須為真||(OR)—— 至少一個條件為真即可!(NOT)—— 反轉條件(true→false,false→true)
# AND( && )
當兩個條件都必須為真時,使用 AND( && ):
測試 a 是否大於 b ,並且 c 是否大於 a :
int a = 200; | |
int b = 33; | |
int c = 500; | |
if (a > b && c > a) { | |
cout << "Both conditions are true"; | |
} |
# OR( || )
當至少一個條件為真即可時,使用 OR( || ):
測試 a 是否大於 b ,或者 a 是否大於 c :
int a = 200; | |
int b = 33; | |
int c = 500; | |
if (a > b || a > c) { | |
cout << "At least one condition is true"; | |
} |
# NOT( ! )
NOT 運算子( ! )會反轉一個條件:
- 如果條件為
true,!會讓它變成false。 - 如果條件為
false,!會讓它變成true。
這在你想要確認某件事「並非如此」時很有用:
測試 a 是否不大於 b :
int a = 33; | |
int b = 200; | |
if (!(a > b)) { | |
cout << "a is NOT greater than b"; | |
} |
# 實際案例
在實際的程式中,邏輯運算子經常用於存取權限控管。例如,要取得系統存取權限,會有特定的條件:
必須已經登入,並且需要是管理員,或是擁有較高的安全等級(1 級或 2 級):
bool isLoggedIn = true; | |
bool isAdmin = false; | |
int securityLevel = 3; // 1 = highest | |
if (isLoggedIn && (isAdmin || securityLevel <= 2)) { | |
cout << "Access granted."; | |
} else { | |
cout << "Access denied."; | |
} | |
// Try changing securityLevel and isAdmin to test different outcomes: | |
// securityLevel 1 = Access granted | |
// securityLevel 2 = Access granted | |
// securityLevel 3 = Access denied | |
// securityLevel 4 = Access denied | |
// If isAdmin = true, access is granted. |
# 實際案例集
這個範例展示如何使用 if...else ,在使用者輸入正確代碼時「打開一扇門」:
int doorCode = 1337; | |
if (doorCode == 1337) { | |
cout << "Correct code.\nThe door is now open.\n"; | |
} else { | |
cout << "Wrong code.\nThe door remains closed.\n"; | |
} |
這個範例展示如何使用 if...else 判斷一個數字是正數還是負數:
int myNum = 10; // Is this a positive or negative number? | |
if (myNum > 0) { | |
cout << "The value is a positive number.\n"; | |
} else if (myNum < 0) { | |
cout << "The value is a negative number.\n"; | |
} else { | |
cout << "The value is 0.\n"; | |
} |
判斷一個人的年齡是否足以投票:
int myAge = 25; | |
int votingAge = 18; | |
if (myAge >= votingAge) { | |
cout << "Old enough to vote!\n"; | |
} else { | |
cout << "Not old enough to vote.\n"; | |
} |
判斷一個人的年齡是否足以投票,並且是否為公民(使用巢狀 if 陳述式):
int age = 20; | |
bool isCitizen = true; | |
if (age >= 18) { | |
cout << "Old enough to vote.\n"; | |
if (isCitizen) { | |
cout << "And you are a citizen, so you can vote!\n"; | |
} else { | |
cout << "But you must be a citizen to vote.\n"; | |
} | |
} else { | |
cout << "Not old enough to vote.\n"; | |
} |
判斷一個數字是偶數還是奇數:
int myNum = 5; | |
if (myNum % 2 == 0) { | |
cout << myNum << " is even.\n"; | |
} else { | |
cout << myNum << " is odd.\n"; | |
} |
檢查溫度(攝氏):
int temperature = 30; | |
if (temperature < 0) { | |
cout << "It's freezing!\n"; | |
} else if (temperature < 20) { | |
cout << "It's cool.\n"; | |
} else { | |
cout << "It's warm.\n"; | |
} |
# C++ Switch
使用 switch 陳述式,從多個程式碼區塊中選擇一個來執行。
語法:
switch(expression) { | |
case x: | |
// code block | |
break; | |
case y: | |
// code block | |
break; | |
default: | |
// code block | |
} |
運作方式如下:
switch運算式只會被計算一次- 這個運算式的值會與每個
case的值進行比較 - 如果找到相符的值,就會執行對應的程式碼區塊
break與default關鍵字是可選的,會在本節稍後說明
以下範例使用星期數字來計算星期名稱:
int day = 4; | |
switch (day) { | |
case 1: | |
cout << "Monday"; | |
break; | |
case 2: | |
cout << "Tuesday"; | |
break; | |
case 3: | |
cout << "Wednesday"; | |
break; | |
case 4: | |
cout << "Thursday"; | |
break; | |
case 5: | |
cout << "Friday"; | |
break; | |
case 6: | |
cout << "Saturday"; | |
break; | |
case 7: | |
cout << "Sunday"; | |
break; | |
} | |
// Outputs "Thursday" (day 4) |
# break 關鍵字
當 C++ 執行到 break 關鍵字時,就會跳出 switch 區塊。
這會停止繼續執行區塊內其餘的程式碼與 case 判斷。
一旦找到相符的結果、任務完成了,就該使用 break,不需要再進行其餘的測試。
break 可以節省大量的執行時間,因為它會「忽略」執行 switch 區塊中剩下的所有程式碼。
# default 關鍵字
default 關鍵字用來指定在沒有任何 case 相符時要執行的程式碼:
int day = 4; | |
switch (day) { | |
case 6: | |
cout << "Today is Saturday"; | |
break; | |
case 7: | |
cout << "Today is Sunday"; | |
break; | |
default: | |
cout << "Looking forward to the Weekend"; | |
} | |
// Outputs "Looking forward to the Weekend" |
# C++ 迴圈(Loops)
只要指定的條件成立,迴圈就能重複執行一段程式碼。
迴圈很方便,因為它們能節省時間、減少錯誤,並讓程式碼更容易閱讀。
# C++ While 迴圈
只要指定的條件為 true , while 迴圈就會重複執行一段程式碼:
語法:
while (condition) { | |
// code block to be executed | |
} |
以下範例中,只要變數 i 小於 5,迴圈內的程式碼就會不斷重複執行:
int i = 0; | |
while (i < 5) { | |
cout << i << "\n"; | |
i++; | |
} |
注意: 別忘了要增加條件中使用的變數( i++ ),否則迴圈永遠不會結束!
你可能好奇為什麼常用字母 i 作為變數名稱?這是一個計數器變數,在簡單迴圈中很常見,因為它簡短、傳統,且代表 'index'(索引)或 'iterator'(疊代器)。
# 倒數計時範例
這個範例從 3 倒數到 1,最後印出 "Happy New Year!!":
int countdown = 3; | |
while (countdown > 0) { | |
cout << countdown << "\n"; | |
countdown--; | |
} | |
cout << "Happy New Year!!\n"; |
# Do/While 迴圈
do/while 迴圈是 while 迴圈的一種變形。這種迴圈會先執行一次程式碼區塊,再檢查條件是否為真,接著只要條件為真就會持續重複執行。
語法:
do { | |
// code block to be executed | |
} | |
while (condition); |
注意: while 條件後面的分號 ; 是必要的!
# Do/While 範例
以下範例使用 do/while 迴圈。因為程式碼區塊會在條件測試之前先執行,所以這個迴圈至少會執行一次,即使條件一開始就是 false。
int i = 0; | |
do { | |
cout << i << "\n"; | |
i++; | |
} | |
while (i < 5); |
別忘了要增加條件中使用的變數( i++ ),否則迴圈永遠不會結束!
# 條件一開始就是 False
上面的範例中,條件 i < 5 一開始就是 true,所以迴圈重複執行了多次。但如果條件一開始就是 false 呢?
以下範例中,變數 i 一開始就是 10 ,所以條件 i < 5 一開始就是假的 —— 但 do/while 迴圈仍然會執行一次:
int i = 10; | |
do { | |
cout << "i is " << i << "\n"; | |
i++; | |
} while (i < 5); |
# 小結
do/while 迴圈至少會執行一次,即使條件一開始就是假的。這與一般的 while 迴圈不同, while 迴圈如果一開始條件就是假的,就會完全跳過整個迴圈。
這個特性讓 do/while 在你希望某件事至少發生一次時很有用,例如顯示訊息或要求使用者輸入。
# 實際案例:使用者輸入
這個範例會不斷要求使用者輸入一個正數,當使用者輸入 0 或負數時迴圈就會停止:
int number; | |
do { | |
cout << "Enter a positive number: "; | |
cin >> number; | |
} while (number > 0); |
# 實際案例
展示 while 迴圈的實際應用,這裡建立了一個簡單的「倒數計時」程式:
int countdown = 3; | |
while (countdown > 0) { | |
cout << countdown << "\n"; | |
countdown--; | |
} | |
cout << "Happy New Year!!\n"; |
這個範例建立一個只印出 0 到 10(含)之間偶數的程式:
int i = 0; | |
while (i <= 10) { | |
cout << i << "\n"; | |
i += 2; | |
} |
這個範例使用 while 迴圈來反轉一些數字:
// A variable with some specific numbers | |
int numbers = 12345; | |
// A variable to store the reversed number | |
int revNumbers = 0; | |
// Reverse and reorder the numbers | |
while (numbers) { | |
// Get the last number of 'numbers' and add it to 'revNumbers' | |
revNumbers = revNumbers * 10 + numbers % 10; | |
// Remove the last number of 'numbers' | |
numbers /= 10; | |
} | |
cout << "Reversed numbers: " << revNumbers << "\n"; |
展示 while 迴圈結合 if else 陳述式的實際應用,假設我們正在玩一場快艇骰子(Yatzy)遊戲:
int dice = 1; | |
while (dice <= 6) { | |
if (dice < 6) { | |
cout << "No Yatzy\n"; | |
} else { | |
cout << "Yatzy!\n"; | |
} | |
dice = dice + 1; | |
} |
如果迴圈跑過 1 到 5 之間的值,就印出 "No Yatzy";每當跑到值 6 時,就印出 "Yatzy!"。
# C++ For 迴圈
當你確切知道要重複執行一段程式碼多少次時,可以使用 for 迴圈來取代 while 迴圈:
語法:
for (statement 1; statement 2; statement 3) { | |
// code block to be executed | |
} |
陳述式 1 會在程式碼區塊開始執行之前執行(一次)。
陳述式 2 定義了執行程式碼區塊的條件。
陳述式 3 會在程式碼區塊每次執行完之後執行。
# 印出數字
以下範例會印出數字 0 到 4:
for (int i = 0; i < 5; i++) { | |
cout << i << "\n"; | |
} |
範例解說
- 陳述式 1 在迴圈開始前設定一個變數:
int i = 0 - 陳述式 2 定義了迴圈執行的條件:
i < 5。如果條件為真,迴圈會再重新執行一次;如果為假,迴圈就會結束。 - 陳述式 3 會在迴圈內的程式碼區塊每次執行後,增加一個值:
i++
# 印出偶數
這個範例印出 0 到 10 之間的偶數:
for (int i = 0; i <= 10; i = i + 2) { | |
cout << i << "\n"; | |
} |
# 數字加總
這個範例計算 1 到 5 之間數字的總和:
int sum = 0; | |
for (int i = 1; i <= 5; i++) { | |
sum = sum + i; | |
} | |
cout << "Sum is " << sum; |
# 倒數計時
這個範例印出從 5 到 1 的倒數:
for (int i = 5; i > 0; i--) { | |
cout << i << "\n"; | |
} |
# 巢狀迴圈(Nested Loops)
也可以把一個迴圈放進另一個迴圈裡面,這稱為巢狀迴圈(nested loop)。
「內層迴圈」會在「外層迴圈」的每一次迭代中執行一次:
// Outer loop | |
for (int i = 1; i <= 2; ++i) { | |
cout << "Outer: " << i << "\n"; // Executes 2 times | |
// Inner loop | |
for (int j = 1; j <= 3; ++j) { | |
cout << " Inner: " << j << "\n"; // Executes 6 times (2 * 3) | |
} | |
} |
# 九九乘法表範例
這個範例使用巢狀迴圈印出一個簡單的乘法表(1 到 3):
for (int i = 1; i <= 3; i++) { | |
for (int j = 1; j <= 3; j++) { | |
cout << i * j << " "; | |
} | |
cout << "\n"; | |
} |
結果:
1 2 3
2 4 6
3 6 9
巢狀迴圈適合用在處理表格、矩陣或多維資料結構時。
# foreach 迴圈
還有一種「for-each 迴圈」(也稱為範圍式 for 迴圈,ranged-based for loop),用來走訪陣列(或其他資料結構)中的元素:
語法:
for (type variableName : arrayName) { | |
// code block to be executed | |
} |
以下範例使用「for-each 迴圈」輸出陣列中的所有元素:
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
for (int num : myNumbers) { | |
cout << num << "\n"; | |
} |
# 走訪字串
也可以使用 for-each 迴圈來走訪字串中的字元:
string word = "Hello"; | |
for (char c : word) { | |
cout << c << "\n"; | |
} |
注意: 如果還看不太懂上面的範例也沒關係,之後在 Arrays 章節會詳細說明陣列。
補充知識: for-each 迴圈是在 C++11(2011 年)版本中引入的。
# 實際案例
展示 for 迴圈的實際應用,建立一個以 10 為間隔數到 100 的程式:
for (int i = 0; i <= 100; i += 10) { | |
cout << i << "\n"; | |
} |
這個範例建立一個只印出 0 到 10(含)之間偶數的程式:
for (int i = 0; i <= 10; i = i + 2) { | |
cout << i << "\n"; | |
} |
這裡我們只印出奇數:
for (int i = 1; i <= 10; i = i + 2) { | |
cout << i << "\n"; | |
} |
這個範例印出 2 的次方直到 512:
for (int i = 2; i <= 512; i *= 2) { | |
cout << i << "\n"; | |
} |
這個範例建立一個程式,印出指定數字的乘法表:
int number = 2; | |
int i; | |
// Print the multiplication table for the number 2 | |
for (i = 1; i <= 10; i++) { | |
cout << number << " x " << i << " = " << number * i << "\n"; | |
} |
# C++ Break 與 Continue
前面章節已經看過 break 陳述式的用法,它用來「跳出」 switch 陳述式。
break 陳述式也可以用來跳出迴圈。
以下範例在 i 等於 4 時跳出迴圈:
for (int i = 0; i < 10; i++) { | |
if (i == 4) { | |
break; | |
} | |
cout << i << "\n"; | |
} |
# C++ Continue
continue 陳述式會在符合指定條件時中斷(迴圈的)一次迭代,並繼續進行下一次迭代。
這個範例會跳過值 4:
for (int i = 0; i < 10; i++) { | |
if (i == 4) { | |
continue; | |
} | |
cout << i << "\n"; | |
} |
# 在 While 迴圈中使用 Break 與 Continue
break 與 continue 也可以用在 while 迴圈中:
# Break 範例
int i = 0; | |
while (i < 10) { | |
cout << i << "\n"; | |
i++; | |
if (i == 4) { | |
break; | |
} | |
} |
# Continue 範例
int i = 0; | |
while (i < 10) { | |
if (i == 4) { | |
i++; | |
continue; | |
} | |
cout << i << "\n"; | |
i++; | |
} |
# C++ 陣列(Arrays)
陣列用來在單一變數中儲存多個值,而不用為每個值宣告個別的變數。
要宣告陣列,需定義變數型態,接著指定陣列名稱、後面加上方括號,並指定要儲存的元素數量:
string cars[4]; |
現在我們宣告了一個可以存放四個字串的陣列變數。要插入值,可以使用陣列字面值(array literal)—— 把值用逗號分隔,放在大括號內:
string cars[4] = {"Volvo", "BMW", "Ford", "Mazda"}; |
要建立一個含有三個整數的陣列,可以這樣寫:
int myNum[3] = {10, 20, 30}; |
# 存取陣列元素
透過方括號 [] 內的索引值來存取陣列元素。
以下陳述式存取 cars 中第一個元素的值:
string cars[4] = {"Volvo", "BMW", "Ford", "Mazda"}; | |
cout << cars[0]; | |
// Outputs Volvo |
注意: 陣列索引從 0 開始:[0] 是第一個元素,[1] 是第二個元素,以此類推。
# 修改陣列元素
要修改特定元素的值,可以透過索引值來指定:
cars[0] = "Opel"; |
string cars[4] = {"Volvo", "BMW", "Ford", "Mazda"}; | |
cars[0] = "Opel"; | |
cout << cars[0]; | |
// Now outputs Opel instead of Volvo |
# 走訪陣列(Arrays and Loops)
可以使用 for 迴圈走訪陣列元素。
以下範例輸出 cars 陣列中的所有元素:
// Create an array of strings | |
string cars[5] = {"Volvo", "BMW", "Ford", "Mazda", "Tesla"}; | |
// Loop through strings | |
for (int i = 0; i < 5; i++) { | |
cout << cars[i] << "\n"; | |
} |
這個範例把每個元素的索引與值一起輸出:
string cars[5] = {"Volvo", "BMW", "Ford", "Mazda", "Tesla"}; | |
for (int i = 0; i < 5; i++) { | |
cout << i << " = " << cars[i] << "\n"; | |
} |
這個範例展示如何走訪一個整數陣列:
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
for (int i = 0; i < 5; i++) { | |
cout << myNumbers[i] << "\n"; | |
} |
# foreach 迴圈
還有一種「for-each 迴圈」(在 C++11(2011 年)引入),專門用來走訪陣列(以及其他資料結構,如 vector 與 list)中的元素:
語法:
for (type variableName : arrayName) { | |
// code block to be executed | |
} |
以下範例使用「for-each 迴圈」輸出陣列中的所有元素:
走訪整數:
// Create an array of integers | |
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
// Loop through integers | |
for (int num : myNumbers) { | |
cout << num << "\n"; | |
} |
走訪字串:
// Create an array of strings | |
string cars[5] = {"Volvo", "BMW", "Ford", "Mazda", "Tesla"}; | |
// Loop through strings | |
for (string car : cars) { | |
cout << car << "\n"; | |
} |
# 省略陣列大小
在 C++ 中,不一定要指定陣列的大小。編譯器能根據插入的值的數量,自動判斷陣列的大小:
string cars[] = {"Volvo", "BMW", "Ford"}; // Three array elements |
上面的範例等同於:
string cars[3] = {"Volvo", "BMW", "Ford"}; // Also three array elements |
不過,後者的寫法被認為是「較佳做法」,因為它能降低程式出錯的機率。
# 宣告時省略元素
也可以在宣告陣列時不指定元素,稍後再加入:
string cars[5]; | |
cars[0] = "Volvo"; | |
cars[1] = "BMW"; | |
cars[2] = "Ford"; | |
cars[3] = "Mazda"; | |
cars[4] = "Tesla"; |
注意: 上面的範例只有在已指定陣列大小時才能運作。
如果沒有指定陣列大小,就會發生錯誤:
string cars[]; // Array size is not specified | |
cars[0] = "Volvo"; | |
cars[1] = "BMW"; | |
cars[2] = "Ford"; | |
cars[3] = "Mazda"; | |
cars[4] = "Tesla"; | |
// error: array size missing in 'cars' |
# 固定大小(陣列)vs. 動態大小(向量)
在討論 C++ 陣列時,經常會聽到「固定大小」與「動態大小」這兩個詞。
這是因為 C++ 中陣列的大小是固定的,代表在陣列建立之後,你無法新增或移除元素。
陣列 —— 固定大小範例:
// An array with 3 elements | |
string cars[3] = {"Volvo", "BMW", "Ford"}; | |
// Trying to add another element (a fourth element) to the cars array will result in an error | |
cars[3] = "Tesla"; |
# 向量(Vectors)
對於需要新增與移除陣列元素的操作,C++ 提供了向量(vectors),也就是可調整大小的陣列。
向量的大小是動態的,代表它可以依需求增長或縮小。
向量位於 <vector> 函式庫中,並提供許多實用的函式來新增、移除與修改元素:
向量 —— 動態大小範例:
// A vector with 3 elements | |
vector<string> cars = {"Volvo", "BMW", "Ford"}; | |
// Adding another element to the vector | |
cars.push_back("Tesla"); |
這只是對向量的簡單介紹,讓你知道「可調整大小的陣列」是存在的。如果還看不懂上面的語法也沒關係,之後的章節會詳細說明向量與其他資料結構。
# 取得陣列大小
要取得陣列的大小,可以使用 sizeof() 運算子:
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
cout << sizeof(myNumbers); |
為什麼結果顯示的是 20 而不是 5 ,明明陣列只有 5 個元素?
這是因為 sizeof() 運算子回傳的是型態的大小,單位是位元組(bytes)。
在 Data Types 章節學過, int 型態通常是 4 個位元組,所以上面範例中,4 x 5(4 位元組 x 5 個元素)= 20 個位元組。
要找出陣列中有多少個元素,必須將陣列的總大小除以陣列第一個元素的大小:
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
int getArrayLength = sizeof(myNumbers) / sizeof(myNumbers[0]); | |
cout << getArrayLength; |
# 用 sizeof () 走訪陣列
在「走訪陣列」章節中,我們把陣列大小直接寫在迴圈條件裡( i < 5 )。這不是理想的做法,因為它只適用於特定大小的陣列。
不過,利用上面範例中的 sizeof() 方法,我們可以寫出適用於任何大小陣列的迴圈,這樣更具持續性。
與其寫成:
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
for (int i = 0; i < 5; i++) { | |
cout << myNumbers[i] << "\n"; | |
} |
更好的寫法是:
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
for (int i = 0; i < sizeof(myNumbers) / sizeof(myNumbers[0]); i++) { | |
cout << myNumbers[i] << "\n"; | |
} |
注意,在 C++11(2011 年)中,也可以使用「for-each」迴圈,寫法更簡潔清楚:
int myNumbers[5] = {10, 20, 30, 40, 50}; | |
for (int num : myNumbers) { | |
cout << num << "\n"; | |
} |
了解走訪陣列的不同方式是有幫助的,因為在不同的程式中都可能會遇到它們。
# 實際案例
展示陣列的實際應用,建立一個計算不同年齡平均值的程式:
// An array storing different ages | |
int ages[8] = {20, 22, 18, 35, 48, 26, 87, 70}; | |
float avg, sum = 0; | |
int i; | |
// Get the length of the array | |
int length = sizeof(ages) / sizeof(ages[0]); | |
// Loop through the elements of the array | |
for (int age : ages) { | |
sum += age; | |
} | |
// Calculate the average by dividing the sum by the length | |
avg = sum / length; | |
// Print the average | |
cout << "The average age is: " << avg << "\n"; |
這個範例建立一個程式,找出不同年齡中最小的年齡:
// An array storing different ages | |
int ages[8] = {20, 22, 18, 35, 48, 26, 87, 70}; | |
int i; | |
// Create a variable and assign the first array element of ages to it | |
int lowestAge = ages[0]; | |
// Loop through the elements of the ages array to find the lowest age | |
for (int age : ages) { | |
if (lowestAge > age) { | |
lowestAge = age; | |
} | |
} | |
// Print the lowest age | |
cout << "The lowest age is: " << lowestAge << "\n"; |
# 多維陣列(Multi-Dimensional Arrays)
多維陣列是「陣列的陣列」。
要宣告多維陣列,需定義變數型態,接著指定陣列名稱,後面加上方括號指定主陣列有多少元素,再加上另一組方括號指定子陣列有多少元素:
string letters[2][4]; |
與一般陣列一樣,可以用陣列字面值來插入值 —— 用逗號分隔、放在大括號中的清單。在多維陣列中,陣列字面值裡的每個元素本身又是另一個陣列字面值。
string letters[2][4] = { | |
{ "A", "B", "C", "D" }, | |
{ "E", "F", "G", "H" } | |
}; |
陣列宣告中的每一組方括號都會為陣列增加一個維度。像上面這樣的陣列稱為擁有兩個維度。
陣列可以有任意數量的維度,維度越多,程式碼就越複雜。以下陣列有三個維度:
string letters[2][2][2] = { | |
{ | |
{ "A", "B" }, | |
{ "C", "D" } | |
}, | |
{ | |
{ "E", "F" }, | |
{ "G", "H" } | |
} | |
}; |
# 存取多維陣列的元素
要存取多維陣列的元素,需要在陣列的每個維度中都指定一個索引值。
以下陳述式存取 letters 陣列中 ** 第一列(0)與第三行(2)** 的元素值:
string letters[2][4] = { | |
{ "A", "B", "C", "D" }, | |
{ "E", "F", "G", "H" } | |
}; | |
cout << letters[0][2]; // Outputs "C" |
記住: 陣列索引從 0 開始:[0] 是第一個元素,[1] 是第二個元素,以此類推。
# 修改多維陣列的元素
要修改元素的值,需在每個維度中指定該元素的索引值:
string letters[2][4] = { | |
{ "A", "B", "C", "D" }, | |
{ "E", "F", "G", "H" } | |
}; | |
letters[0][0] = "Z"; | |
cout << letters[0][0]; // Now outputs "Z" instead of "A" |
# 走訪多維陣列
要走訪多維陣列,需要為陣列的每一個維度各設一個迴圈。
以下範例輸出 letters 陣列中的所有元素:
string letters[2][4] = { | |
{ "A", "B", "C", "D" }, | |
{ "E", "F", "G", "H" } | |
}; | |
for (int i = 0; i < 2; i++) { | |
for (int j = 0; j < 4; j++) { | |
cout << letters[i][j] << "\n"; | |
} | |
} |
這個範例展示如何走訪一個三維陣列:
string letters[2][2][2] = { | |
{ | |
{ "A", "B" }, | |
{ "C", "D" } | |
}, | |
{ | |
{ "E", "F" }, | |
{ "G", "H" } | |
} | |
}; | |
for (int i = 0; i < 2; i++) { | |
for (int j = 0; j < 2; j++) { | |
for (int k = 0; k < 2; k++) { | |
cout << letters[i][j][k] << "\n"; | |
} | |
} | |
} |
# 為什麼需要多維陣列?
多維陣列非常適合用來表示網格。以下範例展示一個實際應用:使用多維陣列來表示簡易版的戰艦(Battleship)遊戲:
// We put "1" to indicate there is a ship. | |
bool ships[4][4] = { | |
{ 0, 1, 1, 0 }, | |
{ 0, 0, 0, 0 }, | |
{ 0, 0, 1, 0 }, | |
{ 0, 0, 1, 0 } | |
}; | |
// Keep track of how many hits the player has and how many turns they have played in these variables | |
int hits = 0; | |
int numberOfTurns = 0; | |
// Allow the player to keep going until they have hit all four ships | |
while (hits < 4) { | |
int row, column; | |
cout << "Selecting coordinates\n"; | |
// Ask the player for a row | |
cout << "Choose a row number between 0 and 3: "; | |
cin >> row; | |
// Ask the player for a column | |
cout << "Choose a column number between 0 and 3: "; | |
cin >> column; | |
// Check if a ship exists in those coordinates | |
if (ships[row][column]) { | |
// If the player hit a ship, remove it by setting the value to zero. | |
ships[row][column] = 0; | |
// Increase the hit counter | |
hits++; | |
// Tell the player that they have hit a ship and how many ships are left | |
cout << "Hit! " << (4-hits) << " left.\n\n"; | |
} else { | |
// Tell the player that they missed | |
cout << "Miss\n\n"; | |
} | |
// Count how many turns the player has taken | |
numberOfTurns++; | |
} | |
cout << "Victory!\n"; | |
cout << "You won in " << numberOfTurns << " turns"; |
# C++ 結構(Structures /struct)
結構(structures,也稱為 structs)是一種把數個相關變數集合在一起的方法。
結構中的每個變數稱為結構的一個成員(member)。
與陣列不同,結構可以包含許多不同的資料型態: int 、 string 、 bool 等等。
# 建立結構
要建立結構,使用 struct 關鍵字,並在大括號內宣告每個成員。
宣告之後,指定結構變數的名稱(以下範例中的 myStructure):
struct { // Structure declaration | |
int myNum; // Member (int variable) | |
string myString; // Member (string variable) | |
} myStructure; // Structure variable |
# 存取結構成員
要存取結構的成員,使用點語法( . ):
賦值給結構的成員並印出它:
// Create a structure variable called myStructure | |
struct { | |
int myNum; | |
string myString; | |
} myStructure; | |
// Assign values to members of myStructure | |
myStructure.myNum = 1; | |
myStructure.myString = "Hello World!"; | |
// Print members of myStructure | |
cout << myStructure.myNum << "\n"; | |
cout << myStructure.myString << "\n"; |
# 一個結構對應多個變數
可以使用逗號( , )讓一個結構套用到多個變數:
struct { | |
int myNum; | |
string myString; | |
} myStruct1, myStruct2, myStruct3; // Multiple structure variables separated with commas |
以下範例展示如何在兩個不同的變數中使用同一個結構:
用一個結構表示兩台車:
struct { | |
string brand; | |
string model; | |
int year; | |
} myCar1, myCar2; // We can add variables by separating them with a comma here | |
// Put data into the first structure | |
myCar1.brand = "BMW"; | |
myCar1.model = "X5"; | |
myCar1.year = 1999; | |
// Put data into the second structure | |
myCar2.brand = "Ford"; | |
myCar2.model = "Mustang"; | |
myCar2.year = 1969; | |
// Print the structure members | |
cout << myCar1.brand << " " << myCar1.model << " " << myCar1.year << "\n"; | |
cout << myCar2.brand << " " << myCar2.model << " " << myCar2.year << "\n"; |
# 具名結構(Named Structures)
透過給結構命名,可以把它當作一種資料型態使用。這代表你可以在程式中的任何地方、任何時候,用這個結構建立變數。
要建立具名結構,把結構的名稱放在 struct 關鍵字後面:
struct car { // This structure is now named "car" | |
string brand; | |
string model; | |
int year; | |
}; |
要宣告使用該結構的變數,就用結構的名稱作為變數的資料型態:
car myCar1; |
現在這個結構可以在任何地方以 car 作為資料型態重複使用:
用一個結構表示兩台車:
// Declare a structure named "car" | |
struct car { | |
string brand; | |
string model; | |
int year; | |
}; | |
int main() { | |
// Create a car structure and store it in myCar1; | |
car myCar1; | |
myCar1.brand = "BMW"; | |
myCar1.model = "X5"; | |
myCar1.year = 1999; | |
// Create another car structure and store it in myCar2; | |
car myCar2; | |
myCar2.brand = "Ford"; | |
myCar2.model = "Mustang"; | |
myCar2.year = 1969; | |
// Print the structure members | |
cout << myCar1.brand << " " << myCar1.model << " " << myCar1.year << "\n"; | |
cout << myCar2.brand << " " << myCar2.model << " " << myCar2.year << "\n"; | |
return 0; | |
} |
# 挑戰任務
建立一個結構來表示一位學生,應包含以下成員:
name(string)age(int)grade(char)
接著:
- 建立一個該結構的變數。
- 賦值給它的成員。
- 將這些值印到螢幕上。
# 預期輸出
Name: Liam
Age: 35
Grade: A
# 解答範例
#include <iostream> | |
#include <string> | |
using namespace std; | |
struct student { | |
string name; | |
int age; | |
char grade; | |
}; | |
int main() { | |
student s1; | |
s1.name = "John"; | |
s1.age = 35; | |
s1.grade = 'A'; | |
cout << "Name: " << s1.name << "\n"; | |
cout << "Age: " << s1.age << "\n"; | |
cout << "Grade: " << s1.grade << "\n"; | |
return 0; | |
} |
# C++ 列舉(Enumeration /enum)
enum 是一種特殊型態,用來表示一組常數(不可變更的值)。
要建立 enum,使用 enum 關鍵字,接著是 enum 的名稱,並用逗號分隔各個項目:
enum Level { | |
LOW, | |
MEDIUM, | |
HIGH | |
}; |
注意最後一項不需要加逗號。
不一定要用大寫,但這通常被視為較佳做法。
Enum 是 "enumerations" 的縮寫,意思是「明確列舉出來」。
要存取 enum,必須先建立一個該 enum 的變數。
在 main() 方法內,指定 enum 關鍵字,接著是 enum 的名稱( Level ),再接著是 enum 變數的名稱(本範例中的 myVar ):
enum Level myVar; |
現在已經建立了一個 enum 變數( myVar ),可以賦值給它。
賦予的值必須是 enum 中的其中一個項目( LOW 、 MEDIUM 或 HIGH ):
enum Level myVar = MEDIUM; |
預設情況下,第一個項目( LOW )的值是 0 ,第二個( MEDIUM )是 1 ,以此類推。
如果現在嘗試印出 myVar,會輸出 1 ,代表 MEDIUM :
int main() { | |
// Create an enum variable and assign a value to it | |
enum Level myVar = MEDIUM; | |
// Print the enum variable | |
cout << myVar; | |
return 0; | |
} |
# 改變數值
如你所知,enum 的第一個項目值為 0,第二個為 1,以此類推。
為了讓這些數值更有意義,可以輕鬆地改變它們:
enum Level { | |
LOW = 25, | |
MEDIUM = 50, | |
HIGH = 75 | |
}; | |
int main() { | |
enum Level myVar = MEDIUM; | |
cout << myVar; // Now outputs 50 | |
return 0; | |
} |
注意,如果為某一個項目指定了值,後面的項目也會依照該值往後遞增:
enum Level { | |
LOW = 5, | |
MEDIUM, // Now 6 | |
HIGH // Now 7 | |
}; |
# 在 Switch 陳述式中使用 Enum
Enum 經常用在 switch 陳述式中,用來檢查對應的值:
enum Level { | |
LOW = 1, | |
MEDIUM, | |
HIGH | |
}; | |
int main() { | |
enum Level myVar = MEDIUM; | |
switch (myVar) { | |
case 1: | |
cout << "Low Level"; | |
break; | |
case 2: | |
cout << "Medium level"; | |
break; | |
case 3: | |
cout << "High level"; | |
break; | |
} | |
return 0; | |
} |
# 為什麼、以及何時使用 Enum?
Enum 用來為常數賦予名稱,讓程式碼更容易閱讀與維護。
當你有一些確定不會改變的值時,就適合使用 enum,例如月份的天數、星期、顏色、一副撲克牌等等。
# C++ 參考(References)
# 建立參考
參考變數是既有變數的別名(alias),使用 & 運算子建立:
string food = "Pizza"; // food variable | |
string &meal = food; // reference to food |
現在,可以用 food 或 meal 來指向同一個值:
string food = "Pizza"; | |
string &meal = food; | |
cout << food << "\n"; // Outputs Pizza | |
cout << meal << "\n"; // Outputs Pizza |
注意: food 與 meal 都指向同一個記憶體位置,修改其中一個也會影響另一個。
# 透過參考更新值
如果修改參考變數的值,原本的變數也會跟著改變(反之亦然),因為它們指向同一個記憶體位置:
string food = "Pizza"; // food variable | |
string &meal = food; // reference to food | |
meal = "Burger"; // changes both meal and food | |
cout << food << "\n"; // Outputs Burger | |
cout << meal << "\n"; // Outputs Burger |
# C++ 記憶體位址(Memory Address)
在前一節的範例中, & 運算子被用來建立參考變數。但它也可以用來取得變數的記憶體位址,也就是該變數在電腦中儲存的位置。
在 C++ 中建立變數時,會為該變數分配一個記憶體位址。當我們賦值給該變數時,這個值就會被儲存在這個記憶體位址中。
要存取它,使用 & 運算子,結果會代表該變數儲存的位置:
string food = "Pizza"; | |
cout << &food; // Outputs 0x6dfed4 |
注意: 記憶體位址是以十六進位形式(0x..)表示。你在自己的程式中得到的結果可能會不一樣。
# 為什麼要知道記憶體位址?
參考(References)與指標(Pointers)(下一節會學到)在 C++ 中很重要,因為它們讓你有能力直接操作電腦記憶體中的資料 —— 這能減少程式碼並提升效能。
這兩個特性是 C++ 與其他程式語言(如 Python 與 Java)與眾不同的地方之一。
# C++ 指標(Pointers)
# 建立指標
前一節學到,我們可以用 & 運算子取得一個變數的記憶體位址:
string food = "Pizza"; // A food variable of type string | |
cout << food; // Outputs the value of food (Pizza) | |
cout << &food; // Outputs the memory address of food (0x6dfed4) |
而指標(pointer)則是一種把記憶體位址當作自己的值來儲存的變數。
指標變數指向一個相同型態的資料型態(如 int 或 string ),使用 * 運算子建立。你所操作的變數的位址會被賦值給這個指標:
string food = "Pizza"; // A food variable of type string | |
string* ptr = &food; // A pointer variable, with the name ptr, that stores the address of food | |
// Output the value of food (Pizza) | |
cout << food << "\n"; | |
// Output the memory address of food (0x6dfed4) | |
cout << &food << "\n"; | |
// Output the memory address of food with the pointer (0x6dfed4) | |
cout << ptr << "\n"; |
# 範例解說
建立一個名為 ptr 的指標變數,透過星號 * ( string* ptr )來指向一個 string 變數。注意,指標的型態必須與你所操作的變數型態相符。
使用 & 運算子取得名為 food 的變數的記憶體位址,並將它賦值給指標。
現在, ptr 儲存的就是 food 的記憶體位址。
小提示: 宣告指標變數有三種寫法,但第一種是較常用的寫法:
string* mystring; // Preferred | |
string *mystring; | |
string * mystring; |
# 取值(Dereference)
前一節的範例中,我們使用指標變數搭配 & 參考運算子來取得變數的記憶體位址。不過,也可以使用指標搭配 * 運算子(取值(dereference)運算子)來取得變數的值:
string food = "Pizza"; // Variable declaration | |
string* ptr = &food; // Pointer declaration | |
// Reference: Output the memory address of food with the pointer (0x6dfed4) | |
cout << ptr << "\n"; | |
// Dereference: Output the value of food with the pointer (Pizza) | |
cout << *ptr << "\n"; |
注意, * 符號在這裡可能會讓人混淆,因為它在程式碼中做了兩件不同的事:
- 在宣告時使用(
string* ptr),它會建立一個指標變數。 - 不在宣告時使用,則作為取值運算子。
# 修改指標的值
也可以修改指標的值,但要注意這也會改變原始變數的值:
string food = "Pizza"; | |
string* ptr = &food; | |
// Output the value of food (Pizza) | |
cout << food << "\n"; | |
// Output the memory address of food (0x6dfed4) | |
cout << &food << "\n"; | |
// Access the memory address of food and output its value (Pizza) | |
cout << *ptr << "\n"; | |
// Change the value of the pointer | |
*ptr = "Hamburger"; | |
// Output the new value of the pointer (Hamburger) | |
cout << *ptr << "\n"; | |
// Output the new value of the food variable (Hamburger) | |
cout << food << "\n"; |
# C++ 記憶體管理(Memory Management)
記憶體管理是控制程式使用多少記憶體、以及如何使用記憶體的過程。這包括在不再需要記憶體時,建立、使用與釋放記憶體。
# C++ 中的記憶體
當你在 C++ 中建立一個變數時,編譯器會自動為它保留記憶體空間。
例如:
int myNumber = 10; |
上面這行告訴程式:「我需要空間來儲存一個整數。」C++ 會自動處理這個記憶體,所以在這種情況下,不需要擔心記憶體管理的問題。
# 取得記憶體大小
可以使用 sizeof 運算子,檢查某個變數型態使用了多少記憶體:
#include <iostream> | |
using namespace std; | |
int main() { | |
int myInt; | |
float myFloat; | |
double myDouble; | |
char myChar; | |
cout << sizeof(myInt) << "\n"; // 4 bytes (typically) | |
cout << sizeof(myFloat) << "\n"; // 4 bytes | |
cout << sizeof(myDouble) << "\n"; // 8 bytes | |
cout << sizeof(myChar) << "\n"; // 1 byte | |
return 0; | |
} |
了解各型態的大小有助於寫出更有效率的程式碼,尤其是在較大型的程式中。
# 需要自己管理記憶體嗎?
有時候需要,有時候不需要。
- 對於一般的變數(如
int x = 10;),C++ 會自動幫你處理記憶體。 - 但當你想在程式執行期間手動建立記憶體時(例如根據使用者輸入),就需要自己管理,並在用完之後自行清理。
如果程式使用了過多的記憶體,或忘記清理不再需要的記憶體,可能會導致效能變慢,甚至當機。
這就是為什麼理解記憶體很重要,它能讓你有更多的控制權,並幫助避免常見的錯誤。
# 接下來呢?
在 C++ 中,可以使用指標直接存取與修改記憶體。
這個功能很強大,但也有風險。如果指標使用不當,可能會意外修改或破壞程式其他部分的記憶體。
下一節會學到如何使用指標,搭配 new 關鍵字手動建立記憶體,並使用 delete 來釋放它。
# C++ new 與 delete
# new 關鍵字
new 關鍵字讓你可以自己管理記憶體。
以下範例中,我們使用 new 為一個整數建立記憶體空間,將值 35 存進去,並使用指標印出它:
int* ptr = new int; | |
*ptr = 35; | |
cout << *ptr; |
說明:
new int為一個整數建立記憶體空間ptr儲存該空間的位址*ptr = 35;儲存數字 35cout << *ptr;印出這個值
所以我們用 new 來建立記憶體,用 ptr 來存取它。
# delete 關鍵字
當你用 new 建立了東西之後,用完之後要負責把它移除。
要做到這件事,使用 delete :
delete ptr; |
這會告訴 C++:「我已經用完這塊記憶體了,你現在可以清理它了。」
# 如果忘記 delete 會怎樣?
如果忘記刪除記憶體,程式仍然可以執行,但隨著時間過去,它可能會使用越來越多的記憶體。
這稱為記憶體洩漏(memory leak),長期下來可能會讓程式變慢,甚至當機。
# 對陣列使用 new 與 delete
也可以使用 new 關鍵字來建立動態陣列。
注意: 對於陣列,要使用 new[] 與 delete[] ;對於單一變數,則使用 new 與 delete 。
當你事先不知道陣列的大小時,動態陣列就很有用 —— 例如陣列的大小取決於使用者輸入,或是程式一開始還不知道的其他數值。
舉例來說,假設你經營一間旅館。因為不知道會有多少位客人到來,所以你詢問使用者人數,並依照這個數字建立相對應數量的房間 —— 每個房間用來儲存一位客人的名字:
#include <iostream> | |
#include <string> | |
using namespace std; | |
int main() { | |
int numGuests; | |
cout << "How many guests? "; | |
cin >> numGuests; | |
// Check for invalid input | |
if (numGuests <= 0) { | |
cout << "Number of guests must be at least 1.\n"; | |
return 0; | |
} | |
// Create memory space for x guests (an array of strings) | |
string* guests = new string[numGuests]; | |
// Ignore the leftover newline character after reading numGuests | |
cin.ignore(); | |
// Enter guest names | |
for (int i = 0; i < numGuests; i++) { | |
cout << "Enter name for guest " << (i + 1) << ": "; | |
getline(cin, guests[i]); // Read the full name (including spaces) | |
} | |
// Show all guests | |
cout << "\nGuests checked in:\n"; | |
for (int i = 0; i < numGuests; i++) { | |
cout << guests[i] << "\n"; | |
} | |
delete[] guests; // Clean up memory | |
return 0; | |
} |
# 何時使用 new
大多數情況下,不需要使用 new 。對於一般變數,C++ 會自動處理記憶體,例如:
int age = 35; | |
string name = "John"; |
但有時候,你必須自己管理記憶體,尤其是在以下情況:
- 事先不知道需要多少記憶體(例如有多少位客人或分數)
- 想在程式執行期間,根據使用者輸入來建立記憶體
- 需要儲存大量或彈性大小的資料
- 想要完全手動控制記憶體(例如效能要求高的程式碼)
在這些情況下, new 可以幫你建立記憶體,而 delete 可以幫你在用完之後清理它。
小提示: 如果使用了 new ,務必記得使用 delete (陣列則用 delete[] ),以避免記憶體洩漏。
# C++ 函式(Functions)
函式是一段程式碼區塊,只有在被呼叫時才會執行。
你可以把資料(稱為參數,parameters)傳入函式。
函式用來執行特定的動作,對於重複使用程式碼來說非常重要:定義一次程式碼,就可以多次使用。
# 建立函式
C++ 提供了一些預先定義好的函式,例如用來執行程式碼的 main() 。但你也可以建立自己的函式來執行特定的動作。
要建立(常稱為_宣告_)一個函式,需指定函式的名稱,後面加上括號 ():
語法:
void myFunction() { | |
// code to be executed | |
} |
# 範例解說
myFunction()是函式的名稱void代表這個函式沒有回傳值,下一節會詳細說明回傳值- 在函式內部(函式主體)加入定義該函式該做什麼的程式碼
# 呼叫函式
宣告好的函式不會立刻執行。它們會被「儲存起來以供之後使用」,等到被呼叫時才會執行。
要呼叫函式,寫出函式名稱,後面加上一對括號 () 與分號 ;
以下範例中, myFunction() 在被呼叫時用來印出一段文字(這個動作):
在 main 中呼叫 myFunction() :
// Create a function | |
void myFunction() { | |
cout << "I just got executed!"; | |
} | |
int main() { | |
myFunction(); // call the function | |
return 0; | |
} | |
// Outputs "I just got executed!" |
一個函式可以被呼叫多次:
void myFunction() { | |
cout << "I just got executed!\n"; | |
} | |
int main() { | |
myFunction(); | |
myFunction(); | |
myFunction(); | |
return 0; | |
} | |
// I just got executed! | |
// I just got executed! | |
// I just got executed! |
# 函式的宣告與定義
C++ 的函式由兩個部分組成:
- 宣告(Declaration): 回傳型態、函式名稱,以及參數(如果有的話)
- 定義(Definition): 函式的主體(要執行的程式碼)
void myFunction() { // declaration | |
// the body of the function (definition) | |
} |
注意: 如果像 myFunction() 這樣的自訂函式,宣告的位置在 main() 函式之後,就會發生錯誤:
int main() { | |
myFunction(); | |
return 0; | |
} | |
void myFunction() { | |
cout << "I just got executed!"; | |
} | |
// Error |
不過,為了讓程式碼優化,是可以把函式的宣告與定義分開的。
你經常會看到 C++ 程式把函式宣告放在 main() 的上方,而函式定義放在 main() 的下方,這樣可以讓程式碼組織得更好、更容易閱讀:
// Function declaration | |
void myFunction(); | |
// The main method | |
int main() { | |
myFunction(); // call the function | |
return 0; | |
} | |
// Function definition | |
void myFunction() { | |
cout << "I just got executed!"; | |
} |
# C++ 函式參數(Function Parameters)
# 參數與引數(Parameters and Arguments)
可以把資訊以參數的形式傳遞給函式。參數在函式內部的作用就像變數一樣。
參數寫在函式名稱後面的括號內。可以加入任意數量的參數,只需用逗號分隔:
語法:
void functionName(parameter1, parameter2, parameter3) { | |
// code to be executed | |
} |
以下範例的函式接收一個名為 fname 的 string 參數。呼叫函式時,我們傳入一個名字,函式內部會用它來印出全名:
void myFunction(string fname) { | |
cout << fname << " Refsnes\n"; | |
} | |
int main() { | |
myFunction("Liam"); | |
myFunction("Jenny"); | |
myFunction("Anja"); | |
return 0; | |
} | |
// Liam Refsnes | |
// Jenny Refsnes | |
// Anja Refsnes |
當一個參數(parameter)被傳遞給函式時,它被稱為一個引數(argument)。所以在上面的範例中: fname 是參數,而 Liam 、 Jenny 與 Anja 則是引數。