電腦只做一件事情: 運算。
我們所看到的任何酷酷的應用: 不論是網頁動畫、遊戲特效、甚至是 AI 說的話,全部都
是由電腦的運算結果組合而成的。
首先我們來梳理一下各個名詞之間的關聯:
1. 運算分成兩個部分: 運算子 (運算的名稱,如: 加法) 和運算元 (運算的對象,可以是常值,如: 8 或變數)。運算就是對資料進行處理,並產生新的資料。
2. 資料如同上一篇所說的,可以分各種資料型態。
3. 資料的型態會決定電腦可以對它進行什麼運算,以及到底該怎麼算。
對多數的 C++ 內建資料型態 (除了少數的特例,以後會提到) 進行運算的時候,需要滿足以下條件:
同個運算的每個運算元都需要有相同的資料型態,產生的結果也會是同樣的資料型態。舉例來說,整數只能跟整數進行加法,產生的結果也會是整數。
需要注意到,上一篇介紹的 string 並不是內建資料型態,所以它不受限於上述的限制,稍後會對於這個特性做進一步的解釋。
基本上就跟數學計算一樣,用來進行我們熟知的四則運算,共有五種: 加(+
)、減(-
)、乘(*
)、除(/
)、取餘數(%
)。
它們的優先順序就跟數學一樣是先乘除後加減,取餘數的優先度和乘除是一樣的。假如遇到兩個優先度相同的運算,則由左到右進行運算。被小括號 ()
包起來的部分則也會優先計算。
cout << 1 + 2 << '\n';
cout << 0.1 + 2.5 << '\n';
cout << 10 / 2 << '\n';
cout << 3 * (10 - 5) % 2 << '\n';
範例1: 輸入長方形的長和寬,計算出面積
int x, y;
cin >> x >> y;
cout << x * y;
範例2: 輸入圓的半徑,計算出面積
int r;
cin >> r;
cout << 3.14 * r * r;
範例3: 輸入 1 元、5 元、10 元銅板的數量,計算總價值
int num_1, num_5, num_10;
cin >> num_1 >> num_5 >> num_10;
cout << num_1 + 5 * num_5 + 10 * num_10;
C++ 的除法有個需要注意的部分: 執行 cout << 3 / 2 << '\n';
的話會發現運算的結果竟然是 1 而不是 1.5。
先前有提到,對大部分的內建資料型態來說,運算結果的型態必須跟運算元的型態相同,因此在整數的除法中,小數點後的數字會被無條件捨去。
那該怎麼保留小數點後的數字呢? 在回答這個問題前,先來思考一下這行程式會印出什麼?
cout << 0.1 + 2;
在這個例子中,加法運算的左邊是浮點數,右邊卻是整數。
為了維持型態要一致的規則,電腦會將資訊量較少的資料轉換成資訊量較多的資料型態。以這個例子來說,浮點數可以紀錄小數點以後的數字,所以相對於整數而言,浮點數是資訊含量較多的資料型態。
因此電腦實際上會執行以下的程式:
cout << 0.1 + 2.0;
答案是 2.1,且兩個運算元和運算結果都具有相同的資料型態。
假如電腦選擇將浮點數轉換成整數,就需要丟棄小數點後的數字: 變成 0 + 2,答案會是 2,顯然是錯誤的。
回到前面 3 / 2
的例子,為了讓運算結果維持浮點數的型態,我們必須將其中一個運算元轉換成浮點數。可以使用一個 C++ 的內建函式做到這件事情:
cout << static_cast<float>(3) / 2;
static_cast
是 C++ 的函式,它會把輸入的值 (小括號中的內容) 轉換成指定的資料型態 (<>
裡面的型態)。
經過轉換後,原本的運算就會變成 3.0 / 2
, 依照前面提到的資料轉換原則,電腦在進行運算的時候會自動把 2 轉換成 2.0,因此運算的結果就會是 1.5。
注意: 你也可以利用 static_cast
將福點數轉換成整數,但是要記得這麼做可能會遺失小數點後的值,請確定這樣的結果是你預期的。
稍微補充一下,函式 (function) 在概念上跟數學課學到的函數有一點像,給它一組輸入,就能得到對應的輸出: y = F(x1, x2, x3)。我們會在後續的文章中深入介紹函式。
範例: 輸入梯形的上底、下底和高,計算出面積:
int top, bottom, height;
cin >> top >> bottom >> height;
cout << static_cast<float>(top + bottom) * height / 2;
上述例子中,top 和 bottom 作為 static_cast 的輸入,會先進行相加再進行進態轉換,也可以寫成:
int top, bottom, height;
cin >> top >> bottom >> height;
cout << (top + bottom) * static_cast<float>(height) / 2;
但這樣寫的話要記得幫 top 和 bottom 加上小括號,才會優先進行計算。
其實就是比大小,共有六種: 大於(>
)、大於或等於(>=
)、小於(<
)、等於或等於(<=
)、等於 (==
)以及不等於(!=
)。
比較運算的結果為布林值 (bool),只有兩種可能: 真 (true) 和假 (false)。
以下是一些例子:
cout << (1 >= 3) << '\n';
cout << (0.0 != -1.0) << '\n';
cout << ((0.1 + 5) == 5.1) << '\n';
注意: 等於 (==
)是兩個等號組成的,一個等號 (=
) 是給值 (assign),要小心不要搞錯了!
範例1: 判斷使用者輸入的數是否為正數
int x;
cin >> x;
cout << (x > 0);
如果是正數的話會印出 1,否則會印出 0。
範例2: 判斷使用者輸入的數是否為偶數
int x;
cin >> x;
cout << (x % 2 == 0);
如果是偶數的話會印出 1,否則會印出 0。
對布林值進行運算,得到另一個布林值。有三種: 且 (&&
)、或 (||
)、相反 (!
)。
這部分對於非本科的朋友可能會比較陌生,所以講得仔細一點。
且 (&&
): 需要兩個布林運算元。兩個運算元都要為真,運算的結果才會為真。以下列出所有可能的狀況 (假設 x 是布林型態的變數):
x = true && true; // x == true
x = true && false; // x == false
x = false && true; // x == false
x = false && false; // x == false
或 (||
): 需要兩個布林運算元。只要其中一個運算元為真,運算的結果就會為真。以下列出所有可能的狀況:
x = true && true; // x == true
x = true && false; // x == true
x = false && true; // x == true
x = false && false; // x == false
相反 (!
): 只需要一個布林運算元。如果該布林值為真,則運算結果為假,反之如果該布林值為假,則運算結果為真。就是相反過來的意思。
x = !true; // x == false
x = !false; // x == true
範例1: 判斷使用者輸入的數是否為偶數且為正數
int x;
cin >> x;
cout << (x % 2 == 0) && (x > 0);
如果是的話會印出 1,否則會印出 0。
範例2: 判斷使用者輸入的數是否為小於 10 的質數
int x;
cin >> x;
cout << (x == 2) || (x == 3) || (x == 5) || (x == 7);
上面列舉了所有小於 10 的質數,看使用者輸入的數是否為其中一個。
範例3: 輸入一個二位數,判斷是否可以被 3 整除
int num;
cin >> num;
int x = num % 10;
int y = num - x;
cout << ((x + y) % 3 == 0);
說明: x 是個位數,y 是十位數,兩者相加如果能被 3 整除,表示 num 可以被 3 整除。
細心一點的朋友可能會注意到在比較運算的範例中,所有的運算都被額外的小括號給包起來了。會這麼做的原因在於相對比較運算而言, <<
的優先級比較高。
算術運算 (+ - * / %
)、比較運算 (> >= < <= == !=
) 和 <<
都屬於 C++ 的運算子。不同的運算子之間存在優先順序,就跟先乘除後加減一樣。如果在程式中看到奇怪的符號,基本上都是某種運算子。
因此,上述程式需要利用小括號來提升比較運算的優先級,電腦才會先計算 1 >= 3
後,再把結果交給 cout
印在螢幕上。算術運算的話就不需要這麼做,因為它們的優先級比 <<
還要高。
此外,需要特別注意邏輯運算的優先級: 相反 (!
) 最高,再來是且 (&&
),最低的是或 (||
)。和其他運算一樣,你可以使用小括號來把要優先運算的部分包起來,就可以讓本來優先級比較低的運算優先進行運算。猜猜看下列的陳述會印出什麼值:
cout << (true || false) && (true && false);
對 C++ 完整的運算子種類及優先順序有興趣的話,可以參考 C++ Operator Precedence 這個網站。某些運算子會留到之後的文章再介紹。
注意: 如果擔心搞錯優先順序的話,使用小括號也是個不錯的選擇。
其實 =
也是 C++ 定義的運算子,而且 =
的優先順序比任何會產生運算結果的運算子都還低,因此不論變數右邊的運算為何,電腦都會先將它們算完,才會將最終的結果透過 =
運算子存到左邊的變數中。
你也可以把算數運算子跟 =
合併起來使用,變成 +=
、-=
、*=
、/=
、%=
。
指派運算子的左邊跟右邊各可以放一個運算元,其中左邊那個一定要是變數。它們的意思是將左邊變數的值取出來,跟右邊的運算元進行運算,最後將結果存回左邊的變數。
舉幾個例子會比較清楚:
int x = 10;
x += 2;
// x == 12
cout << x << '\n';
x %= 3;
// x == 0
cout << x << '\n';
以 x += 2
為例,可以把它想像成是:
x = x + 2;
其他的指派運算子也是相同的概念。
這樣的語法可以讓程式碼看起來比較簡潔一點。
遞增 (++
) 和遞減 (--
) 運算子是專門用來對變數進行加一或減一的運算子。
它的行為基本上就是:
x = x + 1;
x = x - 1;
它們可以擺在變數的前面或是後面。
前置遞增和遞減的樣子是: ++x
和 --x
。
當其他運算需要使用到它們的結果時,前置遞增和遞減會先完成加一或減一的運算,再將運算後的結果交給其他運算。以下舉個例子說明:
int x = 10, y = 0;
// print 11
cout << ++x << '\n';
// print -1
cout << --y << '\n';
你也可以把 cout << ++x
和 cout << --y
想像成是:
x = x + 1;
cout << x;
y = y - 1;
cout << y;
後置遞增和遞減的樣子是: x++
和 x--
。概念剛好和前置遞增、遞減相反。
當其他運算需要使用到它們的結果時,後置遞增和遞減會先將變數目前的值取出來交給其他運算使用,再將加一或減一的運算結果儲存進變數。以下舉個例子說明:
int x = 10, y = 0;
// print 10
cout << x++ << '\n';
// print 11
cout << x << '\n';
// print 0
cout << y-- << '\n';
// print -1
cout << y << '\n';
你也可以把 cout << x--
和 cout << y++
想像成是:
cout << x;
x = x + 1;
cout << y;
y = y - 1;
注意: 也可以對浮點數型態的變數進行遞增、遞減運算。
儘管 string 不是內建的資料型態,但它仍然支援了一些好用的運算子。以下舉三個常用的運算。
string 是由一堆字元所組成,它的加法也很直觀: 把兩堆字串接在一起,如:
string str1 = "Hello ";
string str2 = "C++!";
// print: Hello C++!
cout << str1 + str2;
和整數及浮點數的加法一樣,string 的加法也可以串在一起:
string str1 = "Say 1\n";
string str2 = "Say 2\n";
string str3 = "Say 1,2,3,4!";
// print:
// Say 1
// Say 2
// Say 1,2,3,4!
cout << str1 + str2 + str3;
此外,string 也可以和 char 進行加法:
string str = "A question mark ";
char c = '?';
// print: A question mark ?
cout << str + c + '\n';
// print: ?A question mark
cout << c + str;
通常可以進行 +
就可以進行 +=
。
利用 +=
可以讓我們用更精簡的語法將更多內容串接在字串後面 (英文是 append):
string str = "Hi!";
// append using + and =
str = str + " I am";
// append using +=
str += " Bob.";
// print: Hi! I am Bob.
cout << str;
我們可以用 == 和 != 來比較兩個字串是否有相同的內容:
string foo = "foo";
string bar = "bar";
// print: 0
cout << (foo == bar) << '\n';
// print: 1
cout << (foo != bar);
這篇文章介紹了一些 C++ 的基本運算子。
static_cast
來進行轉型,進而得到預期的結果。=
的優先級是最低的。此外,我們可以利用小括號把想要先做的運算包起來。1. 算出下列的程式會印出什麼結果,再實際執行看看:
int x = 10;
cout << x / 3 << '\n';
float y = 2 * x;
cout << ++y / 2 << '\n';
bool b = !true || false;
cout << b || true && false;
70 sec = 1 min 10 sec.
。下面是程式的範本,請寫程式計算出正確的 min 和 sec:#include <iostream>
using namespace std;
int main() {
int total_sec = 0;
// input how many seconds
cin >> total_sec;
int min = 0;
int sec = 0;
cout << total_sec << " sec = "
<< min << " min "
<< sec << " sec.";
return 0;
}
10000 sec = 2 hr 46 min 40 sec.
。下面是程式的範本,請寫程式計算出正確的 hour、min 和 sec:#include <iostream>
using namespace std;
int main() {
int total_sec = 0;
// input how many seconds
cin >> total_sec;
int hr = 0;
int min = 0;
int sec = 0;
cout << total_sec << " sec = "
<< hr << " hr "
<< min << " min "
<< sec << " sec.";
return 0;
}
/
與 %
以外的運算,嘗試只用/
與 %
寫出來。假如你使用到了 %
,嘗試在不使用它的情況下寫出來。