C++ 基礎語法
在談論演算法跟資料結構以前,我們要先知道資料是如何被讀取、儲存以及輸出的, 我們從資料儲存的載體——變數開始。
變數
變數是用來儲存資料的容器, 在 C++ 中,變數的宣告方式為:
type variable_name;
其中 type
是變數的資料型態,variable_name
是變數的名稱。
常見的型態有以下幾種:
int
:整數型態,用來儲存整數。long long int
:長整數型態,用來儲存較大的整數。unsigned int
:無符號整數型態,用來儲存非負整數。unsigned long long int
:無符號長整數型態,用來儲存非負較大整數。double
:雙精度浮點數型態,用來儲存小數,像是0.8
或者以科學記號表示的數字,例如1.23e4
代表 。char
:字元型態,用來儲存單一字元,像是'a'
、'b'
、'c'
等等。string
:字串型態,用來儲存一串字元,例如"Hello, World!"
,我們之後會有一個章節專門介紹字串這個複雜的資料型態。bool
:布林型態,用來儲存真或假(true
或false
),常用來表示某個條件是否成立。
int
跟 long long int
的差別在於儲存的數值範圍,
int
通常可以儲存 到 的整數,
我們會用 正負 來表示這個範圍,
而 long long int
則可以儲存更大的整數,範圍是 到 ,
我們會用 正負 來表示這個範圍。
另外,如果 int
和 long long int
前面有 unsigned
,則表示這個變數只能儲存非負整數,
例如 unsigned int
可以儲存 到 的整數。
double
則可以儲存小數,精確位數通常是 15 位數,
我們不特別提及 float
,因為在競程中我們通常使用 double
來儲存小數,
因為 float
的精確度較低,可能會導致計算結果不正確。
無論是 double
還是 float
,它們都使用二進位浮點數表示法來儲存小數,
可以參考 IEEE 754 標準來了解浮點數的儲存方式。
char
則是用來儲存單一字元的型態,
通常用來儲存 ASCII 字元,
例如 'a'、'b'、'c' 等等。
變數的宣告可以同時宣告多個變數,例如:
int a, b, c;
long long int x, y;
double z;
char ch;
我們可以把相同型態的變數放在同一行宣告, 但不同型態的變數必須分開宣告。
變數的命名規則如下:
- 變數名稱只能包含英文字母、數字和底線
_
,不能以數字開頭。 - 變數名稱不能是 C++ 的關鍵字,例如
int
、double
、if
、else
等等。
通常來說,我們會使用有意義的變數名稱來表示變數的用途,
例如 score
、count
、sum
等等,方便我們在除錯的時候可以快速了解每個變數的用途,
當然,也可以說是考量到可讀性,與其他人合作時,能夠更容易理解程式碼。
這裡介紹兩種常見的變數命名風格:
- 駝峰式命名法(Camel Case):例如
myVariableName
,每個單字的首字母大寫,除了第一個單字。 - 蛇形命名法(Snake Case):例如
my_variable_name
,單字之間用底線分隔。 這兩種命名風格都可以使用,主要是看個人或團隊的習慣。
輸入輸出
準備好能夠接受輸入的變數之後,我們就可以開始讀取資料了,
在 C++ 中,我們可以使用 cin
來讀取輸入,使用 cout
來輸出結果。
先介紹一下 C++ 的基本程式碼架構
#include <iostream>
using namespace std;
int main()
{
return 0;
}
在這個程式碼中,我們引入了 iostream
標頭檔,這是 C++ 中用來處理輸入輸出的標頭檔,
標頭檔你可以想成程式裡面的工具箱,裡面有很多我們可以使用的工具,我們需要使用 #include
指令來引入這些工具,不同工具箱裡面有不同的工具,
因為這些工具可能有一樣的名字,但也許它們的功能不同,所以我們需要使用 using namespace std;
來告訴編譯器我們要使用標準命名空間中的工具。
在 main
函式中,我們可以寫下我們的程式碼,
最後使用 return 0;
來表示程式正常結束。
接下來,我們可以使用 cin
來讀取輸入,使用 cout
來輸出結果。
#include <iostream>
using namespace std;
int main()
{
/*
我的
第一個程式
*/
int id; // 宣告一個整數變數 id
cin >> id; // 從標準輸入讀取 id 的值
cout << "Hello, Robot " << id << "!" << '\n'; // 輸出結果
return 0;
}
這裡就是上一章節中範例題目的解題程式碼,
我們宣告了一個整數變數 id
,然後使用 cin
讀取輸入的值,
接著使用 cout
輸出結果。
//
代表單行註解,/* ... */
代表多行註解,
註解是用來說明程式碼的用途或功能,並不會被執行,
但是可以用來提高程式碼的可讀性。
像是 "Hello, Robot " 這樣由多個字元組成的東西我們稱為字串,
在 C++ 中,字串可以用雙引號括起來,
而 <<
是用來將字串和變數連接起來的運算符,
最後使用 '\n'
來輸出換行符號,這樣輸出結果就會在下一行。
這樣我們就完成了第一個 C++ 程式,
可以在競程中讀取輸入並輸出結果。
變數運算
一般的數字有加減乘除四則運算, 在 C++ 中,我們可以使用以下運算符來進行數字運算:
+
:加法運算符-
:減法運算符*
:乘法運算符/
:除法運算符%
:取餘數運算符,例如10 % 3
的結果是1
,因為 10 除以 3 的餘數是 1。 這些運算符可以用來對變數進行運算, 例如:
#include <iostream>
using namespace std;
int main()
{
int a = 10, b = 3; // 宣告兩個整數變數 a 和 b
cout << "a + b = " << a + b << '\n'; // 輸出 a + b 的結果
cout << "a - b = " << a - b << '\n'; // 輸出 a - b 的結果
cout << "a * b = " << a * b << '\n'; // 輸出 a * b 的結果
cout << "a / b = " << a / b << '\n'; // 輸出 a / b 的結果
cout << "a % b = " << a % b << '\n'; // 輸出 a % b 的結果
return 0;
}
在這個程式碼中,我們宣告了兩個整數變數 a
和 b
,然後使用 cout
輸出它們的加減乘除和取餘數的結果。
注意到在 C++ 中,整數除法會自動捨去小數部分,
例如 10 / 3
的結果是 3
,而不是 3.333...
,
如果我們想要得到小數部分的結果,可以將其中一個變數轉換成 double
型態,例如:
#include <iostream>
using namespace std;
int main()
{
int a = 10, b = 3; // 宣告兩個整數變數 a 和 b
cout << "a / b = " << (double)a / b << '\n'; // 將 a 轉換成 double 型態
return 0;
}
這樣就可以得到 3.333...
的結果了。
這裡比較特別的是取模運算符 %
,它的實現在部分語言中可能會有所不同,
在 C++ 中,a % b
的結果是 a
除以 b
的餘數,
例如 10 % 3
的結果是 1
,因為 10 除以 3 的餘數是 1,
但如果 a
或 b
是負數,則結果可能會有所不同,
例如 -10 % 3
的結果是 -1
,因為 -10 除以 3 的餘數是 -1。
如果是在 Python 中,-10 % 3
的結果是 2
,
因為 Python 中的取模運算符會將結果調整到非負數,
Python 中的取模運算符是基於數學定義的,
而 C++ 中的取模運算符則是基於 C 語言的實現,
所以在使用取模運算符時要特別注意。
範例答案
範例答案
#include <iostream>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
cout << a * 15 + b * 20 << '\n';
return 0;
}
變數的作用域
變數的作用域是指變數可以被訪問的範圍, 在 C++ 中,變數的作用域可以分為以下幾種:
- 全域變數:在函式外部宣告的變數,可以在整個程式中被訪問。
- 區域變數:在函式內部宣告的變數,只能在該函式內部被訪問。
下面我們用一個範例來說明變數的作用域:
#include <iostream>
using namespace std;
int globalVar = 10; // 全域變數
int main()
{
cout << "Global Variable: " << globalVar << '\n'; // 輸出全域變數
{
int localVar = 20; // 區域變數
cout << "Local Variable: " << localVar << '\n'; // 輸出區域變數
}
cout << localVar << '\n'; // 嘗試輸出區域變數,這裡會報錯
return 0;
}
localVar
變數在區域內部宣告,所以在區域外部無法訪問,
這樣的程式碼會導致編譯錯誤,因為 localVar
在 main
函式的區域內部宣告,
所以在區域外部無法訪問。
如果我們想要在區域外部訪問變數,可以將變數宣告為全域變數,
但這樣會增加程式的複雜度,通常不建議這麼做,
除非有特殊的需求。
位元運算
在電腦的世界中,所有的資料都是以二進位的形式儲存的, 因此位元運算在 C++ 中也是一個非常重要的概念, 我們先介紹何謂二進位。
二進位
二進位是電腦用來表示資料的基本方式,
它只使用兩個數字:0 和 1,
每個數字稱為一個位元(bit),
而八個位元組成一個位元組(byte),
這是電腦儲存資料的基本單位。
例如,數字 5 在二進位中表示為 101
,
這是因為:
- 1 個 4()
- 0 個 2()
- 1 個 1()
所以 101
就是 。
其實我們平常用的十進位也是一樣的道理, 例如數字 123 在十進位中表示為:
- 1 個 100()
- 2 個 10()
- 3 個 1()
所以
123
就是 。
位元運算
位元運算是指對二進位數字進行的運算, 在 C++ 中,我們可以使用以下運算符來進行位元運算:
&
:位元與運算符(AND)|
:位元或運算符(OR)^
:位元異或運算符(XOR)<<
:左移運算符>>
:右移運算符 這些運算符可以用來對變數進行位元運算,
AND、OR、XOR、NOT 的運算規則如下:
A | B | A & B | A | B | A ^ B |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
AND 運算符 &
會對兩個位元進行與運算,
只有當兩個位元都是 1 時,結果才是 1,
否則結果是 0。
OR 運算符 |
會對兩個位元進行或運算,
只要有一個位元是 1,結果就是 1,
否則結果是 0。
XOR 運算符 ^
會對兩個位元進行異或運算,
只有當兩個位元不相同時,結果才是 1,
否則結果是 0。
NOT 運算符 ~
會對一個位元進行非運算,
如果位元是 1,結果是 0,
如果位元是 0,結果是 1。
當對一個整數進行位元運算時,C++ 會將整數轉換成二進位形式, 然後對每個位元進行運算, 例如:
#include <iostream>
using namespace std;
int main()
{
int a = 5; // 二進位表示為 0000 0101
int b = 3; // 二進位表示為 0000 0011
cout << "a & b = " << (a & b) << '\n'; // 0000 0001 = 1
cout << "a | b = " << (a | b) << '\n'; // 0000 0111 = 7
cout << "a ^ b = " << (a ^ b) << '\n'; // 0000 0110 = 6
return 0;
}
在這個程式碼中,我們宣告了兩個整數變數 a
和 b
,
然後使用位元運算符對它們進行運算,
最後輸出結果。
注意到在 C++ 中,位元運算符的優先級較低,
所以如果要對變數進行位元運算,通常需要使用括號來明確運算順序,
最後,還有兩個位元運算符 <<
和 >>
,
它們分別用來進行左移和右移運算,
左移運算符 <<
會將二進位數字向左移動指定的位數,
右移運算符 >>
會將二進位數字向右移動指定的位數,
例如:
#include <iostream>
using namespace std;
int main()
{
int a = 5; // 二進位表示為 0000 0101
cout << "a << 1 = " << (a << 1) << '\n'; // 0000 1010 = 10
cout << "a >> 1 = " << (a >> 1) << '\n'; // 0000 0010 = 2
return 0;
}
在這個程式碼中,我們將 a
向左移動 1 位,得到的結果是 10,
將 a
向右移動 1 位,得到的結果是 2。
這些位元運算符在處理位元資料時非常有用,
例如在加密、壓縮、圖像處理等領域中都會用到。
你會發現 >>
和 <<
長得跟輸出輸入的運算符一樣,
這是因為 C++ 中的輸入輸出運算符也是使用這兩個運算符,
所以使用括號來明確運算順序是很重要的。
範例答案
範例答案
#include <iostream>
using namespace std;
int main()
{
int b1, b2, b3, b4, b5;
cin >> b1 >> b2 >> b3 >> b4 >> b5; // 讀取五個二進位數字
int decimal = (b1 << 4) | (b2 << 3) | (b3 << 2) | (b4 << 1) | b5;
// 邏輯或運算還原十進位數字
// 將每個二進位數字左移相應的位數,然後使用位元或運算符將它們合併
cout << decimal << '\n'; // 輸出十進位數字
return 0;
}
條件處理
在程式中,我們常常需要根據不同的條件來執行不同的程式碼,
不一定是一次把全部程式碼都執行完,像是我們對於一個數字的輸入,我們可能會需要根據它是奇數還是偶數來執行不同的程式碼,
在 C++ 中,我們可以使用 if
語句來實現條件處理,
if
語句的基本語法如下:
if (condition)
{
// 當 condition 為 true 時執行的程式碼
}
當 condition
為 true
時,程式會執行大括號內的程式碼,
如果 condition
為 false
,則不會執行大括號內的程式碼,
這裡特別的事情是,當要執行的程式碼只有一行時,可以省略大括號,
例如:
#include <iostream>
using namespace std;
int main()
{
int number;
cin >> number; // 讀取一個整數
if(number % 2 == 0) // 如果是偶數
cout << "Even" << '\n'; // 輸出 "Even"
return 0;
}
在這個程式碼中,我們讀取了一個整數 number
,
然後使用 if
語句來判斷它是奇數還是偶數,
如果 number
是偶數(number % 2 == 0
),則輸出 "Even"。
如果我們需要在條件不成立時執行其他程式碼,可以使用 else
語句,
else
語句的基本語法如下:
if(condition)
{
// 當 condition 為 true 時執行的程式碼
}
else
{
// 當 condition 為 false 時執行的程式碼
}
例如:
#include <iostream>
using namespace std;
int main()
{
int number;
cin >> number; // 讀取一個整數
if(number % 2 == 0) // 如果是偶數
cout << "Even" << '\n'; // 輸出 "Even"
else // 如果是奇數
cout << "Odd" << '\n'; // 輸出 "Odd"
return 0;
}
在這個程式碼中,我們使用 else
語句來處理當 number
是奇數的情況,
如果 number
是偶數,則輸出 "Even",否則輸出 "Odd"。
但是我們可能有更多種情況需要處理例如交通號誌就有三種顏色,
這時候我們可以使用 else if
語句,
else if
語句的基本語法如下:
if (condition1)
{
// 當 condition1 為 true 時執行的程式碼
}
else if (condition2)
{
// 當 condition2 為 true 時執行的程式碼
}
else
{
// 當 condition1 和 condition2 都為 false 時執行的程式碼
}
例如:
#include <iostream>
using namespace std;
int main()
{
int signal; // 1 代表紅燈,2 代表黃燈,3 代表綠燈
cin >> signal; // 讀取交通號誌的信號
if (signal == 1) // 如果是紅燈
cout << "Stop" << '\n'; // 輸出 "Stop"
else if (signal == 2) // 如果是黃燈
cout << "Caution" << '\n'; // 輸出 "Caution"
else if (signal == 3) // 如果是綠燈
cout << "Go" << '\n'; // 輸出 "Go"
else // 如果不是紅燈、黃燈或綠燈
cout << "Invalid signal" << '\n'; // 輸出 "Invalid signal"
return 0;
}
在這個程式碼中,我們讀取了一個整數 signal
,
然後使用 if
、else if
和 else
語句來處理不同的交通號誌信號,
如果 signal
是 1,則輸出 "Stop";
如果 signal
是 2,則輸出 "Caution";如果 signal
是 3,則輸出 "Go";
如果 signal
不是 1、2 或 3,則輸出 "Invalid signal"。
這樣我們就可以根據不同的條件來執行不同的程式碼,
另外也可以發現 else if
可以有多個,
這樣我們就可以處理更多種情況,
但要注意的是,else if
的順序是有影響的,
如果有多個 else if
,程式會從上到下依次檢查條件,
一旦找到符合條件的 if
或 else if
,就會執行對應的程式碼,
如果沒有符合條件的 if
或 else if
,則會執行 else
的程式碼。
所以在寫 if
、else if
和 else
語句時,要注意條件的順序,
以確保程式能夠正確地執行。
邏輯運算
在 C++ 中,我們可以使用邏輯運算符來組合多個條件, 邏輯運算符的基本語法如下:
condition1 && condition2 // 邏輯與運算符
condition1 || condition2 // 邏輯或運算符
!condition // 邏輯非運算符
邏輯運算符可以用來組合多個條件, 例如:
#include <iostream>
using namespace std;
int main()
{
int age;
cin >> age; // 讀取年齡
if(age >= 18 && age <= 65) // 如果年齡在 18 到 65 歲之間
cout << "You are eligible to work." << '\n';
else // 如果年齡不在 18 到 65 歲之間
cout << "You are not eligible to work." << '\n';
return 0;
}
在這個程式碼中,我們讀取了一個整數 age
,
然後使用邏輯運算符 &&
來判斷 age
是否在 18 到 65 歲之間,
如果 age
在這個範圍內,則輸出 "You are eligible to work.",
否則輸出 "You are not eligible to work."。
邏輯運算符 &&
是邏輯與運算符,表示兩個條件都必須成立,
而邏輯運算符 ||
是邏輯或運算符,表示只要有一個條件成立即可,
例如:
#include <iostream>
using namespace std;
int main()
{
int age;
cin >> age; // 讀取年齡
if(age < 18 || age > 65) // 如果年齡小於 18 歲或大於 65 歲
cout << "You are not eligible to work." << '\n';
else // 如果年齡在 18 到 65 歲之間
cout << "You are eligible to work." << '\n';
return 0;
}
在這個程式碼中,我們使用邏輯運算符 ||
來判斷 age
是否小於 18 歲或大於 65 歲,
如果 age
小於 18 歲或大於 65 歲,則輸出 "You are not eligible to work.",
否則輸出 "You are eligible to work."。
邏輯運算符 !
是邏輯非運算符,表示將條件取反,
例如:
#include <iostream>
using namespace std;
int main()
{
bool isRaining;
cin >> isRaining; // 讀取是否下雨
if(!isRaining) // 如果沒有下雨
cout << "You can go outside." << '\n';
else // 如果下雨
cout << "You should stay inside." << '\n';
return 0;
}
在這個程式碼中,我們讀取了一個布林變數 isRaining
,
然後使用邏輯運算符 !
來判斷是否沒有下雨,
如果沒有下雨,則輸出 "You can go outside.",
否則輸出 "You should stay inside."。
邏輯運算符在處理多個條件時非常有用, 例如在處理複雜的條件判斷時,可以使用邏輯運算符來組合多個條件, 這樣可以使程式碼更簡潔易讀, 同時也可以提高程式的可維護性。
範例解答
範例解答
#include <iostream>
using namespace std;
int main()
{
int x;
cin >> x;
if(x % 5 == 0)
cout << "Yes" << '\n';
else
cout << "No" << '\n';
return 0;
}
當我們使用 ||
時,如果第一個條件已經為 true
,則不會再檢查第二個條件,
同理,當使用 &&
時,如果第一個條件已經為 false
,則不會再檢查第二個條件,這個特性稱為「最小化求值」(short-circuit evaluation),
這可以提高程式的效率,因為不需要檢查不必要的條件,也是提醒我們條件的順序很重要,有時候可以利用這個特性來避免不必要的計算。
常犯的錯誤
其他程式語言的習慣
當題目說數字 A 的範圍是 1 到 100 時,
如果是 Python 的話,我們可以直接寫 if 1 <= A <= 100:
,
但在 C++ 中,這樣寫會導致邏輯錯誤,
如果你寫成
if(1 <= A <= 100)
{
// ...
}
這樣的話,C++ 會先計算 1 <= A
的結果,
這個結果會是 true
或 false
,
然後再將這個結果與 100
進行比較,
這樣的比較是沒有意義的,
因為 true
和 false
在 C++ 中分別是 1 和 0,
所以這樣的比較會導致邏輯錯誤,
正確的寫法應該是:
if(1 <= A && A <= 100)
{
// ...
}
這樣就可以正確地判斷 A
是否在 1 到 100 的範圍內了,
這裡的 &&
是邏輯與運算符,表示兩個條件都必須成立,
這樣才能進入 if
語句內的程式碼區塊。
使用 =
而不是 ==
在 C++ 中,=
是賦值運算符,用來將右邊的值賦給左邊的變數,
而 ==
是比較運算符,用來比較兩個值是否相等,
如果你在 if
語句中使用了 =
而不是 ==
,那麼程式碼會出現邏輯錯誤,
例如:
#include <iostream>
using namespace std;
int main()
{
int A;
cin >> A; // 讀取一個整數
if(A = 10) // 錯誤:使用了賦值運算符
cout << "A is 10" << '\n';
else
cout << "A is not 10" << '\n';
return 0;
}
在這個程式碼中,我們使用了 =
而不是 ==
,
這樣會導致 if
語句中的條件永遠為 true
,
因為 A = 10
會將 10
賦值給 A
,然後 if
語句會判斷 A
是否為非零值,
所以這個條件永遠為 true
,
這樣就會導致程式碼永遠執行 if
語句內的程式碼,
正確的寫法應該是:
#include <iostream>
using namespace std;
int main()
{
int A;
cin >> A; // 讀取一個整數
if(A == 10) // 正確:使用了比較運算符
cout << "A is 10" << '\n';
else
cout << "A is not 10" << '\n';
return 0;
}
在這個程式碼中,我們使用了 ==
來比較 A
是否等於 10
,
這樣就可以正確地判斷 A
是否等於 10
,
如果 A
等於 10
,則輸出 "A is 10",否則輸出 "A is not 10"。
直接比較浮點數
在 C++ 中,浮點數的比較需要特別小心, 因為浮點數在計算機中是以二進位形式儲存的, 這可能會導致精度問題, 例如:
#include <iostream>
using namespace std;
int main()
{
double x = 0.1 + 0.2; // 這個結果可能不是 0.3
if(x == 0.3) // 直接比較浮點數
cout << "x is equal to 0.3" << '\n';
else
cout << "x is not equal to 0.3" << '\n';
return 0;
}
在這個程式碼中,我們將 0.1
和 0.2
相加,然後將結果與 0.3
進行比較,
這樣的比較可能會導致邏輯錯誤,
因為浮點數的精度問題可能會導致 x
的值不是 0.3
,
所以這樣的比較可能會導致程式碼永遠執行 else
語句內的程式碼,
正確的做法是使用一個小的容忍度來比較浮點數,
例如:
#include <iostream>
using namespace std;
int main()
{
double x = 0.1 + 0.2; // 這個結果可能不是 0.3
double tolerance = 1e-9; // 設定一個小的容忍度
if(x - 0.3 < tolerance && x - 0.3 > -tolerance) // 使用容忍度來比較浮點數
cout << "x is approximately equal to 0.3" << '\n';
else
cout << "x is not approximately equal to 0.3" << '\n';
return 0;
}
在這個程式碼中,我們設定了一個小的容忍度 tolerance
,
然後使用 x - 0.3 < tolerance && x - 0.3 > -tolerance
來比較浮點數,
這樣就可以避免浮點數的精度問題,
確保程式碼能夠正確地判斷 x
是否接近 0.3
,
如果 x
接近 0.3
,則輸出 "x is approximately equal to 0.3",否則輸出 "x is not approximately equal to 0.3"。
小結
在這一章中,我們介紹了 C++ 中的變數宣告、輸入輸出、變數運算、條件處理和邏輯運算, 這些都是 C++ 程式設計的基礎知識, 在競程中,我們經常需要使用這些知識來解決各種問題。
當我們需要更多的控制結構時,可以使用迴圈來重複執行程式碼, 在下一章中,我們將介紹 C++ 中的迴圈結構, 以及如何使用迴圈來解決問題。