1. 引言
當一個人剛開始學程式時,常常會這麼想:「我只是要存放數值,為什麼不能做一個通用的類型 number 就好?」這很正常:大腦在節省能量。但 C++ 是一門喜歡具體化的語言。類型在 C++ 中不是「裝飾」,而是一個約定:需要多少記憶體、哪些值是允許的、輸入/輸出如何運作、以及哪些運算有意義。
想像你把資料放在不同的盒子裡。餅乾用有蓋的盒子,湯用鍋子,空氣…好吧,空氣不能存放,它就像週五晚上的動力一樣會消失。類型就像不同的「容器」,如果你試圖把湯倒進餅乾盒,結果可預期地會很糟。
今天我們會拆解五種常見的基本類型:
- int — 普通有號整數,
- long long — 較大的整數,
- unsigned — 無符號整數,
- double — 浮點數類型,
- char — 單一字元。
同時我們會引入一個很重要的概念:值域(類型能表示的最小與最大邊界)。
類型的大小
與許多程式語言不同,C++ 的整數類型沒有固定大小。這樣設計是為了讓編譯器能與平台(作業系統+處理器)更好地對應。
同一個 C++ 程式可以編譯到 16、32、64 位元的處理器;在 macOS、Windows、Linux 等不同平臺下執行。而它們的整數類型各不相同(這是歷史的結果)。
最初 C++ 有三個整數類型:
- int - 整數類型
- short int(也就是 short)- 短整數類型(約為 int 的一半)
- long int(也就是 long)- 長整數類型(約為 int 的兩倍)
之後又加入了更長的整數 - long long int,即 long long。
目前它們的「常見」長度通常如下:
| 類型 | Windows x64 | macOS x64 |
|---|---|---|
|
2 位元組 | 2 位元組 |
|
4 位元組 | 4 位元組 |
|
4 位元組 | 8 位元組 |
|
8 位元組 | 8 位元組 |
與其使用 long,通常更建議使用 long long — 它能更確定地比 int 大。
如何根據需求選擇類型
很多人會按「寫起來比較短」或「看到範例這樣寫」來選擇類型。但在真實程式碼中,類型本身就是語意的一部分。如果你在程式中看到 bool isAdmin,你幾乎不需要註解就能理解那裡只有兩種狀態。看到 char grade,你會預期像 'A' 或 'B' 之類的字元。如果把一切都用 int,就會變成「垃圾場」:0 可能代表「否」、「錯誤」、「未找到」、「第一個元素」或「尚未輸入」。
2. 整數類型:int、long long、unsigned
int:基本的整數「磚塊」
int 是最常用的整數類型,通常是學習者的起點。當你計算已解題數、天數編號、迴圈計數器、攝氏溫度(無小數)或遊戲分數時,int 幾乎總是足夠的。
有兩件事要理解。第一:int 儲存的是整數,且可以表示負數。第二:int 不是無限的 — 它有邊界,這對理解「值域」很重要。但目前先以實務角度看:int 是「合理大小整數的預設選擇」。
給我們的 StudyBuddy 的一個小範例:
#include <iostream>
int main() {
int tasksSolvedToday = 0;
std::cin >> tasksSolvedToday;
std::cout << tasksSolvedToday << '\n'; // 例如:7
}
這裡 tasksSolvedToday 表示「數量」,語意上是整數。在這個層次上類型能幫助避免誤把 3.14 這種小數存進去 — 編譯器會阻止(這是件好事)。
long long:當數值真的很大時
幾乎每個專案都會遇到某個時刻,普通的 int 不夠用了。通常不是一下子,而是當你開始累積數值:一年的總秒數、總觀看次數、總資料量、賽季總分、日誌操作次數 — 也就是會遠超「一般輸入數字」的情況。
long long 是用於大型整數的類型。它也能表示負數,但重要的是它能儲存比 int 大得多的值。我們現在不必記住精確邊界(那取決於平臺),但要記住這個理念:long long 用於重要且大的數值。
在 StudyBuddy 中加入「歷史總解題數」(讓使用者輸入至今已解題數,程式計算新總和):
#include <iostream>
int main() {
long long totalTasksBefore = 0;
int tasksToday = 0;
std::cin >> totalTasksBefore >> tasksToday;
long long totalAfter = totalTasksBefore + tasksToday;
std::cout << totalAfter << '\n'; // 例如:1050
}
注意這點:每天的輸入可能很小,但累計後會很大。這是常見模式,與會不會寫公式無關,而是關於正確選擇類型。
unsigned:把「負號」從約定裡移除
在 C++ 中,任何整數類型都可以透過加上關鍵字 unsigned 變成無符號(非負)。unsigned int(常寫作單純的 unsigned)表示:「整數且不允許負值」。也就是說,unsigned 的值域會往較大的正數偏移,因為不需要保留位元來表示負號。
對於「數量」這類問題:題目數、使用者數、嘗試次數,聽起來很理想。但要注意一個實務面的問題:程式中有時會用負數作為特殊標誌(例如「錯誤」或「未找到」)。如果你把類型轉成無符號,這種做法就不適用了。目前的簡單規則是:只有當你確信從語意上絕不會有負值,且不需要用負值當特殊信號時,才使用 unsigned。
在 StudyBuddy 中加入「連續學習天數」 — 這是個很適合用非負值表示的例子:
#include <iostream>
int main() {
unsigned streakDays = 0;
std::cin >> streakDays;
std::cout << streakDays << '\n'; // 例如:12
}
目前足以記住:unsigned 在型別上強調「非負」。但這不是「萬能的保護」,只是更嚴格的約定。
3. 小數與字元類型:double 與 char
double:當數字有小數部分時
如果 int 與 long long 是針對整數,double 則是針對帶有小數部分的數字。當題目出現平均值、百分比、係數、帶小數的溫度或帶分角的金額時,整數類型就不夠了。
double 儲存浮點數。它能表示像 3.14、0.5、-12.75 這樣的值。但為了這個彈性,你需要付出代價:這些數在內部以二進位表示,不會是「完美精確」,可能會有小幅誤差。
在 StudyBuddy 中加入一週的平均每日產能:
#include <iostream>
int main() {
double averageTasksPerDay = 0.0;
std::cin >> averageTasksPerDay;
std::cout << averageTasksPerDay << '\n'; // 例如:5.75
}
注意 0.0 — 這表示我們正在處理浮點數。
為何不能只用 int「巧妙地除法」?因為整數除法會捨棄小數部分。例如, 7 / 2 在整數運算中會得到 3,而不是 3.5。如果你需要包含小數的精確結果,就必須使用 double。
也要明白:double 並非「無限精確」的計算器。舉例來說,數字 0.1 在二進位系統中無法精確表示,所以像 0.1 + 0.2 的結果可能是 0.30000000000000004。這不是編譯器的錯,而是浮點表示法的特性。
給新手的實務規則:
- 如果你在計算整數數量(件數、天數、元素)— 使用 int 或 long long;
- 如果你在計算平均值、百分比、係數、小數— 使用 double。
double 表示一個約定:「這裡允許小數值」。和其他類型一樣,它在變數宣告層面就幫助讓程式更易懂。
char:一個字元,以及 'A' 與 "A" 的差別
char 是用來儲存單一字元的類型。它用單引號表示:'A'、'?'、'7'。這與字串 "A"(字串可以包含多個字元,在 C++ 中通常使用 std::string)是本質不同的。
對初學者來說很實用的習慣:看到單引號就想到「一個字元」。看到雙引號就想到「字串/文字」。
在 StudyBuddy 中我們會讓使用者輸入一個單字母的「生產力評等」,例如 A、B、C:
#include <iostream>
int main() {
char grade = 'C';
std::cin >> grade;
std::cout << grade << '\n'; // 例如:A
}
char 非常適合單字元資料:回應代碼 Y/N、評等 A/B/C、退出指令 q、分隔符或單一分類字元。
內部上 char 實際上儲存的是字元代碼,本質上也可以視為一種整數類型,在 C++ 中可以對它做算術運算。
5. 值域(範圍)
現在到本講最重要的哲學性部分 — 但我們會以最實務的方式處理。
電腦在記憶體中儲存資料。記憶體是有限的。因此可以寫入變數的不同值的數量也是有限的。由此得出一個簡單觀念: 每個整數類型都有最小值與最大值。這就是所謂的 值域。
在日常生活中,這類似於老車的里程表:它不是無限的,當數字用盡時它會「回繞」(有時甚至壞掉)。類型也是類似的想法:超出邊界時程式行為會變得不正確。我們現在不打算深入「會如何不正確」的細節(那是另一個主題),但你今天就應該養成這個習慣:選擇類型時要確保值能正確儲存。
為了方便判斷,下面是現代系統上一張實務參考表。這不是「處處保證」,而是心智上的良好指南:
| 類型 | 儲存內容 | 範例值域 |
|---|---|---|
|
|
false / true (0 或 1) |
|
單一字元 | −128 … 127 |
|
有號整數 | −2 147 483 648 … 2 147 483 647 |
|
無符號整數 | 0 … 4 294 967 295 |
|
大型有號整數 | −9 223 372 036 854 775 808 … 9 223 372 036 854 775 807 |
|
有號浮點數 | 約 ±1.7 × 10308(約 15 位有效數字) |
要帶走的概念是:若你在計算「總和」、「累計數量」、「累積秒數」等,通常是 long long 的候選。如果是「旗標/是非題」,使用 bool。若是「單一字母/命令」,使用 char。
6. 類型大小與 sizeof
我們已談到 C++ 的整數類型 沒有固定大小,長度取決於平台與編譯器。那如何查出在當前機器上某個類型的實際大小?
可以使用運算子 sizeof。它會回傳型別或變數佔用的位元組數。
一個最簡單的範例:
#include <iostream>
int main() {
std::cout << sizeof(int) << '\n';
std::cout << sizeof(long long) << '\n';
std::cout << sizeof(double) << '\n';
std::cout << sizeof(char) << '\n';
}
例如你可能會得到:
4
8
8
1
這表示:
- int 佔 4 位元組
- long long 佔 8 位元組
- double 佔 8 位元組
- char 佔 1 位元組
7. 選擇基本類型時的常見錯誤
錯誤 #1:用 int 來存邏輯狀態並製造「魔法數字」。
初學者常寫 int ok = 1;、int ok = 0;,然後在別處突然寫出 if (ok == 2)。問題不是編譯器看不懂,而是人會失去理解。若狀態只有兩個選項,使用 bool 能直接在類型上表達,並避免你「不小心」引入第三種狀態。
錯誤 #2:把 char 當成「短字串」。
有時會想把 "OK" 或 "Hi" 存到 char,因為「只有兩個字元」。但 char 是正好一個字元。如果你需要兩個字元,應使用字串(std::string)。每次問自己:「我需要一個字元,還是文字?」這是個好習慣。
錯誤 #3:把 unsigned 當成教條,因為「數量不會為負」,結果卻又需要負數。
現實很狡猾:今天你存的是「題目數」,明天你可能想用負數表示「未輸入」、「錯誤」或「未找到」。因此一開始不要把 unsigned 視為信仰:只有在確定永遠不會需要負值或負值作為特殊標誌時才使用它。
錯誤 #4:把累計總和存成 int,因為「目前還夠用」。
這是一個經典陷阱。測試資料小時一切正常,之後當你計算「全年」、「累積到目前為止」、「所有使用者的總和」時,數值可能超出範圍。這就是為什麼累計與總和常提前用 long long:這是一個便宜的保險,能省下大量找錯時間。
錯誤 #5:認為值域是「某種理論」,而完全忽視它。
值域不是學術議題,而是記憶體有限的直接結果。如果類型無法表示所需的值,程式即會出錯。即使你現在還不會手算邊界,養成問自己「這個數可能會變得很大嗎?」的習慣,就能讓你更有意識地選擇類型。
GO TO FULL VERSION