先讀語法與例子,再操作互動圖解並完成四選一自測。忘記寫法時回看速查表,最後用中英詞彙卡複習。
把本頁的 hello world 丟給 g++,它會先展開標頭、再翻成組合語言與目的檔,最後產生執行檔。中間檔案的行數與大小會隨編譯器、標準函式庫、平台與選項改變,但四個階段的順序不變。
常見的 C++ 建置方式是在執行前完成編譯與連結。Python 的常見實作會先把原始碼編成位元碼,再交給虛擬機執行;語法錯誤也可能在執行前被發現。兩者的重要差別是型別檢查與執行方式,不能簡化成「一個逐行讀、一個一次讀完」。
| Python | C++ | |
|---|---|---|
| 怎麼跑起來 | 常見實作先產生位元碼,再由虛擬機執行 | 先整份編譯成執行檔,再執行那個檔 |
| 什麼時候發現打錯字 | 語法錯誤可能先被發現;名稱或型別使用錯誤常在執行相關運算時發現 | 編譯時就發現,程式根本跑不起來 |
| 型別 | 執行時才決定,同一個變數可以換型別 | 宣告時就定死,之後不能換 |
| 要不要自己管記憶體 | 不用,有垃圾回收 | 區域物件及標準容器會管理自身生命週期;直接使用 new 時需安排對應釋放(P4) |
| 執行效能 | 取決於實作、工作內容與所用函式庫 | 取決於程式、編譯選項與所用函式庫 |
| 執行需要什麼 | 目標機器上要有直譯器 | 執行檔本身,但換平台要重編 |
C++ 讓型別、物件大小、記憶體配置與複製成本清楚出現在程式中,因此適合用來觀察每項操作實際付出的成本。
這一頁的目標很具體:讓你看懂一支獨立 C++ 程式,以及 g++ -std=c++17 -Wall main.cpp -o main 各部分的作用。
g++ -std=c++17 -Wall main.cpp -o main 拆開來看先看一行完整編譯指令,再逐個認識選項:
| 零件 | 意思 | 不寫會怎樣 |
|---|---|---|
| g++ | GNU 的 C++ 編譯器驅動程式 | — |
| -std=c++17 | 用 C++17 這版語言標準 | 吃到編譯器的預設版本,某些寫法會突然不能用 |
| -Wall | 把所有常見警告都打開 | 少了這組常用警告;編譯器仍可能發出其他診斷 |
| main.cpp | 要編的原始碼 | 沒東西可編 |
| -o main | 產出的執行檔叫 main | 預設叫 a.out |
跑起來就是 ./main(Windows 上是 main.exe)。沒有錯誤與警告時,g++ 通常不印訊息;若只有警告,仍可能產生執行檔,但應先讀懂警告再執行。
Hello, C++! 4
三個地方值得停一下:① #include <cmath> 沒寫的話 sqrt 就是未宣告識別字,編譯直接失敗。② sqrt(16) 印出 4 而不是 4.000000,因為 cout 預設只印有意義的位數。③ return 0 是回報給作業系統的結束碼,0 代表正常結束。
平常那行 g++ main.cpp -o main 是把四步一次做完,中間檔案跑完就丟掉。
前處理通常會讓檔案大幅增長,因為 <iostream> 與 <cmath> 的宣告會被展開。實際行數與檔案大小不應視為固定規格。
編譯診斷通常包含檔名、行與欄;連結診斷常包含 undefined reference,代表某個符號找不到定義。連結訊息也可能附來源位置,不能只靠有無行號判斷。PART 07 會逐一比較。
一般 C++ 執行程式需要 main 作為進入點。下面的標頭、using 宣告與 return 依需求使用:若只寫 int main() {} 也合法,到達 main 結尾等同回傳 0。
| 零件 | 它在做什麼 | Python 對應 |
|---|---|---|
| #include <iostream> | 把 <iostream> 這個標頭檔整份貼進來,於是編譯器才認得 cout | import |
| using namespace std; | 把 std 這個命名空間攤平,讓你可以寫 cout 而不是 std::cout | from x import * |
| int main() | 一般 C++ 程式的進入點,由執行環境呼叫 | if __name__ == "__main__" |
| return 0; | 回報結束碼,0 表示正常。非 0 代表出錯,shell 腳本會看這個值 | sys.exit(0) |
#include 是前處理階段做的,字面上就是文字替換。前處理後常見的大幅膨脹,主要來自標頭檔內容被展開。
| 寫法 | 去哪裡找 | 什麼時候用 |
|---|---|---|
| #include <iostream> | 先找系統的標準路徑 | 標準函式庫:<iostream> <string> <vector> <cmath> |
| #include "greeting.hpp" | 先依實作規則搜尋來源檔附近等位置;找不到時通常再採尖括號形式的搜尋規則 | 專案自己提供的標頭檔 |
專案自己的標頭檔用雙引號引進來。若標頭檔不在目前資料夾,可以用 -I 告訴 g++ 額外的搜尋位置:
先將以下內容另存為 greeting.hpp。這裡定義一個回傳固定文字的小函式;目前只需觀察自訂標頭如何被引入,函式語法會在 P3 展開。
Hello from a local header!
greeting.hpp 與 main.cpp 放在同一資料夾時,可直接用 g++ -std=c++17 main.cpp -o main 編譯;若放在 include/,則使用 -I include 加入搜尋路徑。找不到時會看到 fatal error: greeting.hpp: No such file or directory。
using namespace std; 會把整個標準函式庫的名字帶入目前作用域,容易跟自己的名字相撞。較穩健的寫法是逐個 using std::cout;,或直接保留 std:: 前綴。
using namespace std;標頭檔會被原地貼進每個 include 它的來源檔;若在標頭中攤平命名空間,所有引用者都會被迫接受這項選擇。自訂 .hpp 應保留 std:: 前綴。
這是 Python 轉 C++ 第一個要改的習慣。Python 裡 x 前一行是整數、下一行變字串完全沒問題;C++ 的 int x 從出生到離開範圍都是 int。
| Python(動態型別) | C++(靜態型別) | |
|---|---|---|
| 宣告 | x = 5 | int x = 5; |
| 換型別 | x = "hi" 合法 | x = "hi"; 編譯錯誤 |
| 型別寫在哪 | 不寫,執行時看值決定 | 寫在變數名前面,編譯時決定 |
| 沒給初值 | 不存在這種事,一定要指派 | 區域 int 未初始化時不能直接讀值 |
| 字元 | 沒有單獨的字元型別,長度 1 的字串 | char c = 'A'; 單引號 |
| 字串 | s = "hi" | string s = "hi"; 要 #include <string> |
int n = 42 sizeof = 4 bytes double d = 3.14159 sizeof = 8 bytes char c = A sizeof = 1 bytes bool b = 1 sizeof = 1 bytes string s = David sizeof = 32 bytes (物件本身大小) boolalpha 之後 b = true
sizeof(s) 計算 string 物件本身占用的位元組數,s.size() 計算字串中的字元數。物件大小與字元儲存方式由標準函式庫實作決定;短字串可直接存在物件內,較長的字串可另行配置空間。
一般整數可先考慮 int,先確認資料與運算中間結果是否在其範圍內,必要時改用 long long。浮點數常用 double;float 適用於記憶體或介面限制且精度足夠的情況。範圍與精度應查 <limits>,不靠型別名稱猜測。
面板上的大小與範圍是在這台 64 位元 Linux 上用 sizeof 與 <climits> 實際印出來的。C++ 標準只規定下限,換平台可能不一樣,所以要問大小就用 sizeof,不要背。
未初始化的區域基本型別沒有可依賴的值;在這裡讀取它會產生未定義行為。程式可能看似輸出某個數字,也可能在最佳化後出現完全不同的結果,因此不能把一次試跑當成證據。
錯誤示範:讀取未初始化的 x、y,行為未定義,沒有可依賴的輸出。
在 C++17 中讀取這裡未初始化的 x 或 y 是未定義行為,不能推論為某個殘值,也沒有固定輸出。這是錯誤示範;正確做法是在使用前初始化或賦值,例如 int x = 0;。
例如 int sum; 後直接 sum += 3; 會先讀到未初始化的 sum。應先寫 int sum = 0;,計數器同理寫 int count = 0;。這個問題與數字看起來是否合理無關,關鍵是使用前有沒有確定的值。
int low, high; 一次宣告兩個 int。若寫 int a, b = 5;,只有 b 被初始化,a 仍不能直接讀取;要各自給初值,例如 int a = 0, b = 5;。
cout << 與 cin >>:箭頭指著資料流的方向記法很直觀:cout << x 是把 x 推進輸出串流,cin >> x 是把輸入串流的東西倒出來放進 x。箭頭指哪邊,資料就往哪邊跑。
姓名:David,年齡:20 一行 兩行 123 1 + 2 = 3
第三行印出 123 而不是 1 2 3 —— << 不會自動加空格,要空格自己寫。第四行的 1 + 2 先算完才丟給 <<,因為 + 的優先順序比 << 高。
| 你想做的事 | Python | C++ |
|---|---|---|
| 印一個值並換行 | print(x) | cout << x << endl; |
| 印了不換行 | print(x, end="") | cout << x; |
| 印兩個值中間有空格 | print(a, b) | cout << a << " " << b; |
| 讀一整行 | s = input() | getline(cin, s); |
| 讀一個整數 | n = int(input()) | cin >> n; |
| 印 true / false | print(True) | cout << boolalpha << b; |
endl 與 '\n' 差在哪兩個都換行,但 endl 多做一件事:強迫把緩衝區的東西立刻寫出去。輸出量大的時候(例如印一萬筆排序結果)每行都 endl 會明顯變慢。
| endl | '\n' | |
|---|---|---|
| 換行 | 會 | 會 |
| 清空緩衝區 | 會 | 不會 |
| 速度 | 慢 | 快 |
| 什麼時候用 | 要立刻看到輸出時,例如印 debug 訊息、程式可能會當掉 | 其他所有情況 |
順帶一提:'\n' 是單引號包的一個 char,"\n" 是雙引號包的字串。C++ 的單引號與雙引號不能互換,char c = "A"; 會編譯錯誤。
>> 讀一個詞,getline 讀一整行上面這支需要互動輸入。下面改用固定字串作為輸入來源;對本例的 >> 格式化讀取而言,空白切分與型別轉換規則相同,因此能穩定展示解析結果:
first=[David] last=[Malan] age=20 第一次 getline=[] 第二次 getline=[Hello data structures]
看第二行輸出:第一次 getline 讀到的是空字串。因為 >> 讀完 20 就停了,那個換行還留在串流裡,getline 一讀就碰到它、當場結束。這是初學者最常卡住的地方 —— 解法是在 >> 之後、getline 之前補一行 cin.ignore();,把那個換行丟掉。
>> 以空白分隔,讀不到有空格的名字cin >> name; 碰到 David Malan 只會拿到 David,Malan 留在串流裡等下一次讀。要整行就用 getline(cin, name);。
printf 以格式字串指定欄寬、精度與引數型別,需引入 <cstdio>。cout 依型別選擇輸出方式,也能搭配 <iomanip> 排版。以下保留兩種方式的比較,重點是 printf 的格式必須與引數型別一致。
| 轉換字元 | 對應型別 | 格式示例 |
|---|---|---|
| %d | int | printf("%d 頁", pages); |
| %s | 字串,要先 .c_str() | printf("%s costs %d cents\n", item.c_str(), price); |
| %f %10.7f | double | 10 是欄寬、7 是小數位數,數字自動靠右對齊 |
| %lld | long long | printf("Sum is %lld required %10.7f seconds\n", s, secs); |
The eraser costs 35 cents Sum is 500000500000 required 0.0012345 seconds found(沒加 boolalpha)= 1 found(加了 boolalpha)= true
%s 需要 C 字串,std::string 可用 .c_str() 取得對應指標。不能直接把 string 物件交給 %s。cout 預設將 bool 印成 1 或 0,加入 boolalpha 後會印 true 或 false,這個設定持續生效,直到改用 noboolalpha。
在 Python 3 裡 7 / 2 是 3.5,要整數除法得寫 7 // 2。C++ 沒有 // 這個運算子(那是註解),它的規則只有一句:兩邊都是整數,除法就是整數除法,小數直接丟掉。
7 / 2 = 3 <- 兩邊都是 int,小數被截掉 7 % 2 = 1 <- 餘數 7.0 / 2 = 3.5 <- 有一邊是 double 就變浮點除法 -7 / 2 = -3 <- 往零的方向截,不是往下取整 -7 % 2 = -1 <- 餘數跟被除數同號 1453 / 16 = 90 1453 % 16 = 13 <- 進位制轉換就靠這兩個
四個要記住的點:① 只要有一邊是 double,整條式子就變浮點除法。② -7 / 2 是 -3 不是 -4,C++ 往零的方向截,跟 Python 的 // 往下取整不一樣。③ 因此 -7 % 2 是 -1,餘數跟被除數同號,Python 的 % 則跟除數同號。④ 最後兩行就是進位制轉換的全部:/ 拿到下一輪要處理的數,% 拿到這一位的數字。
| 式子 | Python 3 | C++ | 為什麼 |
|---|---|---|---|
| 7 / 2 | 3.5 | 3 | C++ 兩邊都是 int,做整數除法 |
| 7.0 / 2 | 3.5 | 3.5 | 有一邊是浮點數就做浮點除法 |
| 7 // 2 | 3 | 語法錯誤 | // |
| -7 / 2 | -3.5 | -3 | 往零截斷 |
| -7 // 2 | -4 | — | Python 往下取整 |
| -7 % 2 | 1 | -1 | C++ 的餘數跟被除數同號 |
i++ 印出 5,之後 i = 6 ++j 印出 6,之後 j = 6 total += 5 -> 15 total -= 3 -> 12 total *= 2 -> 24 total /= 4 -> 6 <- 整數除法還是會截
i++ 是先把舊值交出去,再加一;++i 是先加一,再交出新值。單獨寫一行當作敘述時兩者沒差(for 迴圈的 i++ 換成 ++i 結果一樣),差別只在把它塞進別的式子裡的時候。最後一行提醒你:/= 一樣會截斷。
2 + 3 * 4 = 14 (2 + 3) * 4 = 20 10 < 5 = false 10 < 5 < 3 = true <- 先算 10<5 得到 0,再算 0<3
Python 的 10 < 5 < 3 是「同時滿足兩個條件」,答案是假。C++ 沒有這種語法糖,它從左邊算起:10 < 5 得到 false(也就是 0),再算 0 < 3,於是變成真。要表達範圍檢查,一定要寫成 (5 <= x) && (x <= 10)。
| 優先順序(高到低) | 運算子 |
|---|---|
| 1 | () [] -> . |
| 2 | ++ -- ! *(解參考) &(取址) |
| 3 | * / % |
| 4 | + - |
| 5 | << >> |
| 6 | < <= > >= |
| 7 | == != |
| 8 | && |
| 9 | || |
| 10 | = += -= *= /= %= |
不用背整張表,記兩件事就夠:<< 的優先順序比比較運算高,所以 cout << a < b 會編譯錯誤,要寫 cout << (a < b);&& 比 || 高,混用時加括號比較安全。
三個人的分數是 90、85、79,總分 254。int avg = 254 / 3; 之後 avg 是多少?
rand()/srand():靠 % 把亂數壓進範圍rand() 產生 0 到 RAND_MAX 的偽亂數。rand() % 6 + 1 可示範如何將結果映射到 1 到 6:先取餘數,再平移。不過這種映射通常有偏差,不能用來保證公平骰子;本例只教 % 的範圍運算。
每行點數都介於 1 到 6;確切序列依 rand 的實作而異。
不呼叫 srand 時,rand 的行為如同用種子 1 初始化。同一實作使用相同種子可重現相同序列,但不同實作不保證相同。srand(time(0)) 可使用時間作種子,需 <ctime>;在相同秒數啟動仍可能得到相同序列。
C++ 會依運算規則做隱式型別轉換,不是所有情況都只把小型別轉成大型別。此處先掌握一個重點:int / int 先算整數商,即使之後存入 double,已被截掉的小數也不會恢復。
| 式子 | 編譯器怎麼做 | 結果 |
|---|---|---|
| 7 / 2.0 | 把 7 轉成 7.0,做浮點除法 | 3.5 |
| 7 / 2 | 兩邊都是 int,不轉 | 3 |
| double d = 7 / 2; | 先算完 3,才轉成 3.0 | 3 |
| int n = 3.99; | 截斷小數,不是四捨五入 | 3 |
| int n = true; | bool 轉 int | 1 |
| int n = 'A'; | char 轉 int,取 ASCII 碼 | 65 |
第三列最不容易察覺。double avg = sum / n; 看起來完全合理,但等號右邊是兩個 int,除法在轉成 double 之前就做完了。
a / b = 3 (double)(a / b) = 3 <- 先做整數除法才轉,太晚了 static_cast<double>(a) / b = 3.5 <- 先把一邊變成 double,正確 static_cast<int>(3.99) = 3 <- 轉 int 是截斷,不是四捨五入 平均(錯)= 84,平均(對)= 84.3333
第二行的 (double)(a / b) 是典型的錯法:括號包住整個除法,等於「先算出 3,再把 3 變成 3.0」。第三行才對 —— 把其中一邊先變成 double,除法才會用浮點的規則做。只轉一邊就夠,另一邊會被隱式轉換帶上去。
要浮點的答案,轉型要放在除號的左邊、括號的裡面:static_cast<double>(a) / b。放錯位置的 static_cast<double>(a / b) 編得過、跑得動、答案錯,而且錯得很安靜。
對本節的算術型別 x,static_cast<double>(x)、(double)x 與 double(x) 都能轉成 double。這不表示各種轉型語法在所有型別上等價;先使用用途清楚、方便搜尋的 static_cast,閱讀舊程式時再辨認其他形式。
INT_MAX 表示目前實作的 int 上限。有號運算超出範圍是未定義行為,不保證變成負數;無號數的模數繞回則有明確規則。以下把危險運算留在註解中,執行的只有定義良好的部分。
若 int 為 32 位元、long long 為 64 位元: int 範圍:-2147483648 ~ 2147483647 unsigned 0 - 1 = 4294967295 先轉 long long = 2147483648
不要執行 int 的 INT_MAX + 1;應先確認較寬型別能容納結果,再在運算之前轉型。此例使用 long long 前會檢查 LLONG_MAX 是否大於 INT_MAX。0u - 1 則是無號運算,標準保證結果為 UINT_MAX。
INT_MAX 也拿來當「無限大」哨兵INT_MAX 是有限整數,不是真正的無限大。它可以當「尚未找到較小值」的初始標記,例如 int best = INT_MAX;,之後只以較小且有效的觀測值更新。若 INT_MAX 本身也可能是合法答案,還需額外的 bool found 區分;也不可對這個標記隨意加一。
例如計算 n 個項目兩兩配對的數量 n * (n - 1) / 2,中間乘積可能先超出 int。可先轉型:long long total = static_cast<long long>(n) * (n - 1) / 2;,仍須確認 n 的有效範圍與 long long 的容量。賦值給較大型別本身不會補救先前的溢位。
對本例的算術型別,C 式轉型與 static_cast 結果相同。static_cast 將轉型意圖明確寫在程式中,也限制可接受的轉換種類;不要把它當作能修復先前運算溢位的工具。
g++ 的訊息通常會列出檔名、行號、欄號與診斷內容。先修正第一個 error:,接著重新編譯,再檢查其餘訊息;後面的診斷有些會隨第一個錯誤一起消失,也可能仍有需要處理的獨立問題。
| 片段 | 意思 |
|---|---|
| main.cpp | 哪一個檔出事 |
| 6 | 第幾行 |
| 5 | 那一行的第幾個字元(欄) |
| error: | 等級。error 一定要修,warning 編得過但通常也該修 |
| expected ',' or ';' before 'cout' | 錯在哪。這一段才是要讀的東西 |
| In function 'int main()' | 出事的位置在哪個函式裡。函式一多這行就很有用 |
下面保留四類常見錯誤與對應程式,診斷的詳細措辭可能依 g++ 版本而不同。
[編譯失敗]
cell.cpp: In function ‘int main()’:
cell.cpp:6:5: error: expected ‘,’ or ‘;’ before ‘cout’
6 | cout << x << endl;注意它指向第 6 行,但本例漏掉的分號在第 5 行。編譯器到讀見下一個不合語法的 token 才能確定問題,因此診斷位置可能晚於真正原因。看到 expected ';' 時,可先檢查該位置及前一個敘述的結尾。
[編譯失敗]
cell.cpp: In function ‘int main()’:
cell.cpp:7:13: error: cannot convert ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’} to ‘int’ in initialization
7 | int n = name;cannot convert A to B 是最好讀的一類:它明講了「有一個 A,但這裡要的是 B」。中間那串 std::__cxx11::basic_string<char> 是 string 的完整名字,看到 aka 後面才是人話。這一類錯誤在你開始用 template <typename T> 之後會變得又臭又長,訣竅一樣:只讀 cannot convert 那一句。
[編譯失敗]
cell.cpp: In function ‘int main()’:
cell.cpp:6:21: error: ‘conut’ was not declared in this scope; did you mean ‘const’?
6 | total = total + conut;was not declared in this scope 只有三種可能:打錯字(這裡是 conut)、忘了宣告、忘了 #include。g++ 甚至會猜你想打什麼(did you mean),雖然這次猜錯了。如果訊息裡的名字是 cout 或 sqrt 這種標準函式庫的東西,那就是第三種。
還有一類長得完全不一樣,因為它發生在連結階段而不是編譯階段:
[編譯失敗] /usr/bin/ld: main.o: in function `main': main.cpp:(.text+0x13): undefined reference to `addUp(int, int)' collect2: error: ld returned 1 exit status
認得出來的兩個特徵:開頭是 /usr/bin/ld:(ld 是連結器),而且沒有指到你的哪一行。undefined reference to 的意思是「我知道有這個函式,但找不到它的身體」。常見原因是宣告了忘記實作、函式簽章打錯字,或多檔案專案只編了一個 .cpp。
| 看到這句 | 八成是 | 先去哪裡看 |
|---|---|---|
| expected ';' before … | 漏分號 | 它指的那行的上一行 |
| was not declared in this scope | 打錯字/忘了 include | 那個名字的拼字,或檔案開頭 |
| cannot convert A to B | 型別不合 | 等號兩邊、或函式的參數 |
| no matching function for call to … | 參數個數或型別對不上 | 函式的宣告與呼叫處比對 |
| undefined reference to … | 連結階段:只宣告沒定義 | 該函式的實作,或編譯指令少了檔案 |
| No such file or directory | include 的路徑不對 | -I 有沒有加對(見 PART 02) |
| Segmentation fault | 這不是編譯錯誤,是執行時當掉 | 通常是指標,見 P4 |
① 從第一個 error 開始修,修完馬上重編 —— 一個錯常常引發後面二十個。
② 編譯時加 -Wall -Wextra。它能發現許多可疑寫法,例如部分未初始化使用與有號/無號比較;警告能力仍取決於控制流程、編譯器與選項,不能取代測試。
③ 每寫十行就編譯一次。寫完兩百行才第一次按編譯,錯誤會互相糾纏到看不出源頭。
assert,另一種比編譯錯誤更晚一步的檢查上面講的都是編譯期的錯誤;assert(條件) 抓的是執行期的錯誤。條件不成立時,程式立刻中止並印出發生問題的那一行,比讓錯誤值繼續傳遞、到後面才失敗容易追查。
5 [執行結束碼 -6] main: main.cpp:6: int half(int): Assertion `n % 2 == 0' failed.
未定義 NDEBUG 時,half(10) 通過斷言;half(7) 的條件為假,assert 會輸出診斷並呼叫 abort 終止程式,結束碼表示依平台及執行工具而異。若編譯時定義 NDEBUG,assert 會被停用,因此不可用它取代必要的使用者輸入檢查。
目標是印出 7 除以 2 的小數答案,但它印出 3:
g++ 印出 main.cpp:6:5: error: expected ',' or ';' before 'cout',第 6 行是 cout << x << endl;。你應該先做什麼?
| 指令 | 做什麼 |
|---|---|
| g++ -std=c++17 -Wall main.cpp -o main | 四階段一次做完,產出執行檔 main |
| ./main | 執行 |
| g++ -std=c++17 -I include main.cpp -o main | 自訂標頭放在 include/ 時加 -I |
| g++ -E main.cpp | wc -l | 查看目前環境中前處理後的實際行數 |
| g++ -g main.cpp -o main | 帶除錯資訊,之後可以用 gdb ./main |
| 型別 | sizeof | 範圍或精度 | 什麼時候用 |
|---|---|---|---|
| bool | 1 | true / false | 條件、旗標 |
| char | 1 | -128 ~ 127 | 單一字元,單引號 'A' |
| short | 2 | -32,768 ~ 32,767 | 需要較小整數表示範圍的資料 |
| int | 4 | −2,147,483,648 ~ 2,147,483,647 | 整數的預設選擇 |
| unsigned int | 4 | 0 ~ 4,294,967,295 | 只會是非負數時;小心它減到負會繞回 |
| long long | 8 | ±9.22 × 1018 | 會超過二十一億時 |
| float | 4 | 約 6 位有效數字 | 精度足夠且需節省空間時 |
| double | 8 | 約 15 位有效數字 | 小數的預設選擇 |
| int * | 8 | 一個位址 | 此表的環境為 8 bytes;其他實作可能不同,見 P4 |
| string | 32 | 物件本身的大小 | 可能含物件內短字串儲存;長度用 |
這張表是這一頁最該存下來的東西。左邊是你已經會的,右邊是同一件事在 C++ 怎麼寫。
| 要做的事 | Python | C++ | 差在哪 |
|---|---|---|---|
| 註解 | # 一行 | // 一行 /* 多行 */ | C++ 的 // 不是除法 |
| 宣告變數 | x = 5 | int x = 5; | 要寫型別,要分號 |
| 常數 | PI = 3.14 | const double PI = 3.14; | C++ 的 const 是編譯器強制的 |
| 印出並換行 | print(x) | cout << x << endl; | 不會自動加空格 |
| 讀一行輸入 | s = input() | getline(cin, s); | cin >> s 只讀到第一個空白 |
| 定義函式 | def f(a, b): | int f(int a, int b) { } | 回傳型別與參數型別都要寫 |
| 整數除法 | 7 // 2 | 7 / 2 | C++ 的 / 兩邊都是 int 就是整數除法 |
| 浮點除法 | 7 / 2 | static_cast<double>(7) / 2 | 轉型要放在除法之前 |
| 取餘數 | 7 % 2 | 7 % 2 | 負數時符號不同 |
| 次方 | 2 ** 10 | pow(2, 10) | 要 #include <cmath>,而且回傳 double |
| 強制轉整數 | int(3.99) | static_cast<int>(3.99) | 都是截斷 |
| 字串長度 | len(s) | s.length() | C++ 的 string 要 #include <string> |
| 取子字串 | s[2:5] | s.substr(2, 3) | 第二個參數是長度不是結束位置 |
| 可變長度的序列 | a = [1, 2, 3] | vector<int> a = {1, 2, 3}; | 見 P5 |
| 加到尾巴 | a.append(x) | a.push_back(x); | 名字不同,行為一樣 |
| 真假值 | True False | true false | C++ 全小寫;印出來預設是 1 / 0 |
| 程式進入點 | if __name__ == "__main__": | int main() { } | C++ 一定要有,而且只能有一個 |
流程控制(條件、邏輯運算、迴圈)的對照見 P2 的速查表。
| 資源 | 看什麼 |
|---|---|
| C++ Tutor | 把本頁的程式貼進去單步執行,看每個變數的值怎麼變。整數除法那一段特別值得跑一次。 |
| Compiler Explorer | 貼入程式並檢視編譯器產生的組合語言,調整選項可比較結果。 |
| cppreference · 運算子優先順序 | 完整的優先順序表,寫括號猶豫時查這個。 |
| cppreference · Translation phases | 前處理、編譯與字面值等翻譯階段的正式參考。 |
| P0b · 環境設定 | 還沒把 g++ 裝起來的話先看這頁。 |
| 本站第 01 章 · 導論 | 把這一頁的東西實際用起來。 |
每個選項都有解說:選錯也點開看看為什麼錯。全對之後再往下翻詞彙卡。
int a = 7, b = 2; cout << a / b << " " << a % b << endl;
int x = 10; double y = x / 4; cout << y << endl;
cout << boolalpha << (10 < 5 < 3) << endl;
int i = 5; cout << i++ << " "; cout << i << endl;
int a[5] = {1, 2, 3, 4, 5};
cout << sizeof(int) << " " << sizeof(a) << " " << sizeof(a) / sizeof(int) << endl;
先看正面術語,心中默想定義再翻面對答案;洗牌後再過一輪,直到每張都能不假思索說出來。