← 09/10|課程介紹與環境暖身 | 回總覽 | 09/24|流程控制與函式基礎(Ch 2、Ch 3) →
對應課本習題:Ch1: 6, 8, 13;Ch2: 2, 4, 7, 8(Ch2 用到迴圈的題目,等 09/24 上完再寫)
這幾題各要用到什麼(動手前先看)
主課的上機考幾乎就是這些題目,所以每一題都自己寫過。下表是每題需要的東西(我的歸納,不是題目本文)與本系列對應的練習:
| 課本題號 | 要用到的東西 | 先練 |
|---|---|---|
| Ch1-6 | 依區間分段計算費用——if/else if、double 運算 |
Q6、Q8 |
| Ch1-8 | 同一組公式反覆算五次、印到小數兩位——本週先把那幾行複製五次,09/24 學了迴圈再改 | Q1、Q8 |
| Ch1-13 | 兩個數量相除、算要幾份才夠——整數除法、型別 | Q3、Q4 |
| Ch2-2 | 反覆扣款、加息,直到條件成立為止——while 迴圈+邊界檢查(09/24) |
09/24 Q2、Q7 |
| Ch2-4 | 印出範圍內所有質數——巢狀迴圈(09/24) | 09/24 Q1 |
| Ch2-7 | 讀 n 個等第字母換成點數再平均——switch 對 char(本週)+迴圈(09/24) |
Q9 高爾夫、09/24 Q4 |
| Ch2-8 | 從 100 度往下找攝氏=華氏的那一度——迴圈到條件成立(09/24) | 09/24 Q7 |
這週要會什麼
1 | 程式骨架 → 變數與型別(含常數)→ 型別轉換 → 輸入輸出 → 算術運算 |
一支程式的骨架
1 |
|
這支程式可以編譯、可以執行,但畫面上不會有任何東西——因為它什麼事都沒做。
第 ② 行的 std 是標準函式庫用來裝自己所有名字的一個盒子,這種盒子叫命名空間(namespace);cout 的全名其實是 std::cout、cin 是 std::cin(:: 讀作「某某裡面的」)。using namespace std; 等於先跟編譯器說「以下看到不認得的名字,去 std 這個盒子裡找」,所以才能省略前面的 std::。不寫會怎樣你在 09/10 的示範 ③ 看過了:error: 'cout' was not declared in this scope。盒子本身怎麼做出來是 11/26 的主題,現在照抄就好。
// 註解、{ } 區塊、分號這三條規則〈環境設置〉和 09/10 都講過了,這裡只補兩點:
- 分號跟換行無關,一個敘述可以拆成好幾行(Q2 的
cout就是),只要最後有分號就好。 - 本文之後大量用
//標示「這一行會印出什麼」,而且很多程式碼區塊是片段,只寫出要討論的那幾行。想自己編譯來玩,就把它們貼進上面骨架main的{ }裡(用到cout就要有#include <iostream>和using namespace std;);有寫#include和int main()的區塊才是可以直接編譯的完整程式。另外提醒一下:片段常常宣告了變數卻沒真的拿去用,貼進骨架編譯會冒出一串warning: unused variable 'x'——那是片段才會有的現象,不是你打錯字,交作業的完整程式不會出現。
變數與型別
識別字(identifier)命名規則
- 只能由英文字母、數字、底線
_組成,且不能以數字開頭。 - 大小寫有別:
Total、total、TOTAL是三個不同的變數。 - 不能使用保留字:
int、class、return、for、if、new、delete…… - 變數與函式命名寫
totalScore或total_score都可以,但整份程式挑一種用到底,改考卷的人會看風格。(類別、常數的命名慣例等用到的時候再說。)
基本型別
| 型別 | 典型大小 | 說明 | 寫起來長這樣 |
|---|---|---|---|
int |
4 bytes | 整數,約 $\pm 2.1 \times 10^9$ | int n = 42; |
double |
8 bytes | 浮點數(預設用這個),約 15 位有效數字 | double x = 3.14; |
char |
1 byte | 一個字元,實際存的是 ASCII 碼 | char c = 'A'; |
bool |
1 byte | 只有 true / false 兩種值,這種值叫布林值,下面的 if 會大量用到 |
bool ok = true; |
string |
不定 | 字串,長度自動調整,需 #include <string> |
string s = "Yilin"; |
另外還有 long long(裝得下更大的整數,約 $\pm 9.2 \times 10^{18}$;之後幾題數字會大到 int 裝不下,就換它)、unsigned int(不能是負數)、short(更省空間)、float(精度比 double 差),後三個這學期幾乎用不到,知道有這些東西就好。
寫「典型大小」是因為實際大小由編譯器與平台決定,想自己量就在程式裡寫 cout << sizeof(int) << ' ' << sizeof(double);——sizeof(型別) 算出該型別在這台機器上佔幾個 byte,也可以寫 sizeof(變數名);我這台印出 4 8。
<climits> 幫你定義好 INT_MAX / INT_MIN,代表 int 裝得下的最大與最小值(2147483647 與 -2147483648)。要用它們就得多寫一行 #include <climits>——用到哪個函式庫的東西,就 #include 誰,忘了會得到 error: 'INT_MAX' was not declared in this scope。
整數超過範圍不會報錯,這叫溢位(overflow),筆試很愛考。要記對的是這句:有號整數(int、long long)溢位是未定義行為(後面〈未定義行為〉那節會正式介紹)——C++ 標準沒規定結果,編譯器甚至可以假設它不會發生,所以 int x = INT_MAX; x = x + 1; 不能背成「一定變成 -2147483648」;沒開優化時常常看起來像繞回去,開了優化同一段程式可能得到別的答案。只有無號整數(unsigned int)的溢位有明確規定:像里程表一樣繞回 0。考試要答的是「這裡會溢位,結果不可信」,不是某個固定數字。同一類的還有沒給初值的區域變數:int x; 就拿去算,讀到的不只是「某個垃圾值」,整段程式的行為都不再有保證。
宣告與初始化
int age = 18; 這一行拆開來是三件事:型別 int + 名字 age + 初值 18。
- 宣告=跟編譯器登記「我要一個叫
age的int變數」;初始化=宣告的同時就給它第一個值。 =唸作指派(有些書譯作「賦值」),意思是「把右邊算出來的值放進左邊的變數」,跟數學的等號不一樣。所以sum = sum + 10;讀作「把sum現在的值加 10,再存回sum」,是合法而且很常見的寫法。數學那個「兩邊是否相等」在 C++ 要寫兩個等號==,跟=是完全不同的運算子——〈環境設置〉提到的「把==打成=」,就是把「比較」誤寫成「指派」。==在下面〈比較與邏輯運算〉會正式講。
1 | int age = 18; // 宣告同時初始化(建議) |
雷區 ①:未初始化的變數
1
2
3 int sum; // 只宣告、沒給初值 → 裡面是這塊記憶體上次被拿去用時剩下的殘值
sum = sum + 10; // 拿垃圾值去加,結果當然還是垃圾
cout << sum; // 每次執行可能印出不同的數字C++ 不會幫區域變數自動歸零。累加器一定要
int sum = 0;。
-Wall只抓得到最單純的情況,值一旦繞過幾層就未必抓得到——別靠編譯器,宣告就給初值。
const 具名常數
1 | const double PI = 3.14159265358979; |
const 表示「這個變數給了初值之後就不准再改」。好處是改一個地方就全改、編譯器會擋住誤改、程式自我說明(看到 MAX_SIZE 比看到 100 清楚)。課本與課程都建議用 const 而非 #define。
命名慣例:常數習慣全部大寫、字間用底線(PI、MAX_SIZE),一眼就能跟普通變數分開,<climits> 的 INT_MAX 就是這樣取名的。
字面值與跳脫序列
字面值(literal)就是直接寫在程式裡的固定值,編譯器依它的長相決定型別:
1 | 42 // int |
記住 2 是 int、2.0 是 double——待會的整數除法陷阱就是從這裡來的。(數字後面加 LL 表示這個字面值是 long long,11/12 算金額時會用到;看到別的字母後綴不用慌,都是在指定型別。)
常用跳脫序列:
| 寫法 | 意義 |
|---|---|
\n |
換行 |
\t |
Tab |
\\ |
反斜線本身 |
\" |
雙引號 |
\' |
單引號 |
\0 |
空字元(字串結尾標記,11/19 講 C 風格字串時會用到) |
型別轉換
把一種型別的值放進另一種型別的變數,編譯器會自動幫你轉,這叫隱式轉換:
1 |
|
輸出:
1 | 3 5 A 65 1 |
三個要注意的地方:
- 電腦裡沒有「字母」,
char存的是一個小整數,再依一張叫 ASCII 的對照表顯示成字元:'A'是 65、'a'是 97、'0'是 48。所以char c = 65;印出A,int n = 'A';得到 65。 double d = 5;裡面確實是 5.0,但cout預設不顯示沒意義的小數,所以看到5。bool印出來是1和0,不是true/false。筆試的「看程式答輸出」很愛考。
double → int 會損失精度。如果要自己動手、明白寫出「我就是要轉成這個型別」,叫顯式轉換(cast,強制轉型):
1 | double d = 3.99; |
static_cast<想要的型別>(要轉的東西):角括號裡寫目標型別,小括號裡寫來源。這裡的< >跟#include <iostream>的角括號完全沒關係(之後的vector<int>也是這種用法)。(型別)值是 C 留下來的舊寫法,效果一樣,但比較難搜尋、也比較容易誤用。
輸入輸出:cin 與 cout
1 |
|
在終端機輸入 3 5 後,畫面長這樣:
1 | 請輸入兩個整數:3 5 |
cout << x:把x送到標準輸出,<<可以一直串接。cin >> x:從標準輸入讀一個值。會自動跳過前置的空白與換行,然後依x的型別決定讀多少:int/double讀到「不能再當數字的字元」就停(輸入12x會得到 12,x留給下一次讀),char只讀一個非空白字元,string才是讀到下一個空白為止。>>跟<<一樣可以串接,cin >> a >> b;是先讀一個值放進a、再讀下一個放進b,所以鍵盤打3 5(中間空白或換行都可以)會得到a = 3、b = 5。endl= 換行 + 把輸出立刻送出;'\n'只換行。差別在於cout為了效率不會每印一個字就送一次螢幕,而是先堆在緩衝區累積一批再送。日常用'\n'就好,輸出量大時才看得出差。(另外還有cerr,標準錯誤輸出,不緩衝、專門印錯誤訊息,12/03 講串流時會再遇到。)
格式化輸出(需要 #include <iomanip>)。三個之後一定會用到的設定:
1 |
|
輸出(第二行結尾的 | 是我故意印出來標示欄位右界的):
1 | 3.14 |
setw(6)、setprecision(2)這種「名字 + 小括號 + 值」的形狀叫呼叫函式,括號裡是你交給它的值,換個值就換個效果(函式的正式說明在 09/24);fixed則是沒有括號的光禿名字。這些都只是調整cout的設定、不是要印的內容,送進去畫面上不會多任何字——所以可以像 Q8 解答那樣,單獨寫一行cout << fixed << setprecision(3);先把格式設定好,後面再印資料。fixed+setprecision(n):固定小數點後n位,實驗課題目最常要求的格式。少寫fixed的話n會變成「有效位數」(從第一個非 0 的數字開始數的位數),3.14159就會印成3.1。setw(n)是欄寬:印表格時讓每一欄對齊用的,上機考的表格題會用到(助教發的函式庫速查表裡就有它);它只對下一個輸出有效,印完就恢復。setfill(c)換掉補位字元(預設空白),跟fixed一樣設一次就一直有效。對齊方向等 12/03 印報表時再講。
算術運算與整數除法陷阱
先說三個之後會一直出現的名詞:運算子(operator)是做運算的符號;運算元(operand)是被運算的東西(7 / 2 裡的 7 和 2);運算式(expression)是把運算子和運算元接起來、算得出一個值的一串東西(7 / 2、a + b * 2)。
C++ 的算術運算子有五個:+(加)、-(減)、*(乘)、/(除)、%(取餘數)。乘除的優先順序比加減高,跟數學一樣,不確定就加括號。% 只能用在整數,5.5 % 2 直接編譯錯誤。
1 | cout << 7 / 2 << '\n'; // 3 整數 ÷ 整數 = 整數(無條件捨去) |
雷區 ②:整數除法
1
2 int a = 5;
double b = a / 2; // b = 2.0,不是 2.5!
a / 2兩邊都是int,先算出整數 2,才轉成double。正確寫法:double b = a / 2.0;或double b = static_cast<double>(a) / 2;。
平均分數算錯幾乎都是這個原因,是實驗課最常見的扣分點。
複合指定運算子
sum += 5; 就是 sum = sum + 5; 的簡寫——「把自己更新成運算後的結果」:
1 | int sum = 0; |
-= 與 /= 也一樣。作業裡會很常看到,本週練習題 Q4 的解答就用了 %=。
++ 與 --
i++ 與 ++i 都是把 i 加 1(等同 i = i + 1,也等同 i += 1);i-- 與 --i 則是減 1。差別只在這個運算式本身算出來的值是變動前還是變動後:
1 | int i = 5; |
單獨寫成一行 i++; 時前置後置沒有差別,09/24 的 for 迴圈最常這樣用。
未定義行為(UB)
未定義行為(undefined behavior,常簡稱 UB)是這學期最重要的名詞之一:代表 C++ 標準沒有規定這種寫法該產生什麼結果,所以編譯器想怎麼做都可以——可能看起來正常、可能當掉、也可能換台電腦就變另一個答案。之後講陣列越界、指標的時候還會一直遇到。最典型的一種是在同一個運算式裡改同一個變數:
雷區 ③:同一個運算式裡改同一個變數
1
2 int i = 1;
int x = i++ + i++; // 結果未定義,不同編譯器可能給不同答案不要寫這種程式。筆試若出現,答案通常是「undefined behavior」。
比較與邏輯運算
比較運算子:==、!=、<、<=、>、>=
邏輯運算子:&&(且)、||(或)、!(非)
1 | int x = 50, day = 6; // 一行宣告兩個 int,等同 int x = 50; int day = 6; |
優先順序(由高到低):! > 算術運算子 > 比較運算子 > && > || > =
不確定就加括號,沒有人會因為你多加括號扣分。
雷區 ④:連續不等式
1 if (0 < x < 10) // 永遠為真!C++ 會先算
0 < x,得到true(1) 或false(0),再拿 1 或 0 去跟 10 比——1 < 10與0 < 10都成立。正確寫法:if (0 < x && x < 10)。
這招-Wall抓得到:warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses],看到就是中這招。
雷區 ⑤:
=寫成==
1 if (x = 5) { ... } // 這是「把 5 指派給 x」,結果 5 非 0 → 永遠為真
-Wall會提示warning: suggest parentheses around assignment used as truth value。看到這個警告 99% 是打錯字。
前面〈型別轉換〉提過「非 0 即 true」,所以讀別人的程式時 while (n) 就等於 while (n != 0);自己寫建議寫完整。
短路求值(short-circuit evaluation):&& 左邊為 false 就不算右邊;|| 左邊為 true 就不算右邊。
1 | int a = 0, b = 10; |
所以保護條件一定要寫在左邊:寫成 if (b / a > 3 && a != 0) 就沒救了,a 是 0 時 b / a 先被算,整數除以 0 會讓程式當場 Floating point exception (core dumped) 掛掉,右邊的保護來不及生效。
分支:if 與 switch
if / else if / else
最小形式是 if (條件) 敘述A else 敘述B——括號裡的條件(一個布林運算式)算出來是 true 就執行敘述A,是 false 就執行敘述B,else 那半可以整個省略。結果不只兩種時,把下一個 if 接在 else 後面寫成 else if,就變成一串由上往下的檢查:
1 | int score; |
多路 if-else 是由上而下逐一檢查,第一個成立就結束,所以條件要由嚴格排到寬鬆。如果把 score >= 60 寫在第一個,95 分也會拿 D。
用 {} 包起來的一串敘述叫複合敘述(compound statement),語法上算「一個」敘述——if 後面永遠只管一個敘述。只有一行時大括號可以不加,但建議一律加(上面的範例就是照這個規矩寫的):
1 | if (x > 0) |
switch
1 | char op; |
1 | 輸入: 12 * 5 |
(cin >> a >> op >> b 會依序抓走三樣東西,中間的空白自動跳過。)
switch 是另一種分支:拿一個整數(或字元)去對照幾個固定的值,比一長串 else if 好讀。先建立心智模型:switch 不是「從幾個分支裡挑一個」,而是「拿括號裡的值跟每個 case 的常數比對,跳到相符的那一行,然後一路往下執行,直到遇見 break 或大括號結束」;都沒對上就跳到 default:(可省略,省略時什麼都不做)。case 常數:、default: 後面接的是冒號——它們是標記位置的標籤、不是敘述,所以不加分號。另外兩條限制:switch 的條件必須是整數型別、字元或列舉(不能是 double 或 string),而 case 後面必須是常數。
雷區 ⑥:忘記
break
1
2
3
4
5 switch (n) {
case 1: cout << "one";
case 2: cout << "two"; break;
}
// n == 1 時會印出 "onetwo"這段用
g++ -Wall -Wextra編就會被抓到:warning: this statement may fall through [-Wimplicit-fallthrough=],底下還會補一行note: here指出穿到哪裡去。
有時穿透是刻意的(多個 case 共用同一段程式),這時在兩個case之間加一行註解// fall through,警告就會消失——g++ 認得這句英文,它不只是寫給人看的。至於 09/24 練習題 Q4 解答那種「case 10:後面直接接case 9:」的空標籤,中間沒有任何敘述,不算穿透,本來就不會警告。
程式風格
除了 //,還可以用 /* ... */ 包住跨很多行的註解。筆試會看程式可讀性,實驗課助教檢查時也會看,三個基本要求:縮排一致(一層 4 個空格或 1 個 Tab,別混用)、變數名有意義、關鍵步驟有註解。
本週重點回顧
- 宣告變數時就給初值,C++ 不會幫你歸零;
=是「把右邊存進左邊」,不是數學等號。 - 整數除以整數還是整數:
7 / 2得到 3。要浮點結果,至少一邊要是浮點(7 / 2.0)。 x += 3是x = x + 3的簡寫,x++是x += 1的再簡寫;前置後置差在「運算式的值」是變動前還是變動後。- 小數位數用
fixed << setprecision(n)(會一直生效);欄寬用setw(n)(只影響下一個輸出)。 - 型別轉換:
int i = 3.99;直接截掉小數得 3(不是四捨五入);要明講就寫static_cast<double>(x)。期末筆試第 1 題考的就是這個。 - 未定義行為=標準沒規定該怎麼做,結果無法預期,不是「這樣寫也會動」。
- 比較用
==(=是指派);0 < x < 10不是數學的意思,要寫0 < x && x < 10;保護條件放&&左邊。 if後面只管一個敘述,一律加大括號;switch的每個case要break,條件只能是整數或字元。
本週練習題
練習時用
g++ -std=c++11 -Wall -Wextra編譯(〈環境設置〉說的練習用選項),確認零警告。
Q1. BMI 計算
讀入身高(公分,浮點數)與體重(公斤,浮點數),輸出 BMI 到小數點後兩位。
$$\text{BMI} = \frac{\text{體重(kg)}}{\text{身高(m)}^2} $$
1 | 輸入: 175.5 68.2 |
參考解答
1 |
|
Q2. 秒數轉時分秒
讀入一個非負整數 n(秒,保證 0 ≤ n ≤ 359999,也就是時數不會超過兩位),輸出格式為 hh:mm:ss,不足兩位補 0。
1 | 輸入: 3725 |
參考解答
1 |
|
這個 cout 拆成四行寫,但只有最後才有分號——因為它是一個敘述。setfill('0') 設一次就一直有效,setw(2) 則要對每一個輸出各寫一次。
Q3. 數字拆解
讀入一個三位數正整數,分別輸出它的百位、十位、個位數字,以及三個數字的和。
1 | 輸入: 472 |
參考解答
1 |
|
n / 10 % 10 的運算順序是由左到右(/ 與 % 同優先級),先除再取餘。
Q4. 找零錢
讀入一個整數金額(元),用 500、100、50、10、5、1 元由大到小找零,輸出每種各幾張/幾個。
1 | 輸入: 1268 |
參考解答
1 |
|
每一輪都是「除出張數、把餘額留給下一種面額」,最後剩下的就是 1 元的個數。
學完 10/08 的陣列與迴圈之後,這六段重複可以縮成一個迴圈——那時再回來改寫一次。
實驗課題型加練
下面 Q5–Q7 就是實驗課第 1 週(09/17 下午)的三題課堂練習,Q9 是同一週的 Makefile 練習——都不是我出的;這裡用我自己的話重講、範例資料是我自己做的,要求跟課堂投影片相同。做完就是當天檢查的內容,所以 Q5–Q7 只給題目、範例輸出與思路,不放完整程式:實驗課的分數是當場檢查給的,課堂也規定不能用 AI 產生解答,請自己寫完再拿範例對。Q8 的題型來自歷年考古題,附完整解答。
Q5. 奇數還是偶數(實驗課第 1 週課堂練習 1-1)
讀入一個整數,印出它是奇數(odd)還是偶數(even)。
1 | 輸入: 7 |
思路與驗算
- 用
%:n % 2 == 0就是偶數——這是本週%最常見的用途。負數也要對:-4 % 2是 0、但-3 % 2是 -1(不是 1),所以判斷「餘數不等於 0 就是奇數」比判斷「餘數等於 1」安全。 - 驗算:
0→ even、-3→ odd、10→ even。
Q6. 現在在學校嗎(實驗課第 1 週課堂練習 1-2)
週四 9:30 前要到校、16:00 放學。讀入現在的時、分(24 小時制):在校時間內印 At School 並印出距離放學還有幾小時幾分;不在校時間印 Off School;小時不在 0–23 或分不在 0–59 印 Invalid time。
1 | 輸入: 14 45 |
1 | 輸入: 17 5 |
1 | 輸入: 25 0 |
思路與驗算
- 合法性檢查放最前面,不合法就印訊息、
return 0;結束(在main裡return就是結束整支程式),後面就不必再考慮怪輸入。 - 把時、分換算成「從 0:00 起算的總分鐘數」(
h * 60 + m):9:30 是 570、16:00 是 960,「在不在校」就只剩一個判斷now >= 570 && now <= 960。 - 剩下的分鐘數用
/ 60和% 60拆回時、分(Q2 的技巧反過來用)。 - 驗算:
9 30→ At School、left: 6 h 30 m;16 0→ At School、left: 0 h 0 m;9 29→ Off School;12 60→ Invalid time。輸出字串的大小寫、空格要跟題目一模一樣。
Q7. 三個數:眾數與去重(實驗課第 1 週課堂練習 1-3)
讀入三個整數,先印「同一個數字最多重複了幾次」,再把三個數去掉重複後由大到小印出。只用 if 和幾個變數就夠。
1 | 輸入: 5 9 5 |
1 | 輸入: 4 4 4 |
思路與驗算
- 次數只有三種可能:三個都相同是 3、恰有兩個相同是 2、否則 1——一組
if / else if / else就分完(「恰有兩個相同」是a == b || b == c || a == c)。 - 去重的關鍵是先排序再印:用三次「比大小就交換」把三個數排成
a >= b >= c(交換兩個變數的標準寫法是int tmp = a; a = b; b = tmp;,需要第三個變數暫存),排好之後重複的數字一定相鄰——先印a,b跟a不同才印,c跟b不同才印。 - 驗算:
1 2 3→ most: 1、3 2 1;7 7 2→ most: 2、7 2。
Q8. 圓與球
讀入一個直徑 d(double),把它當成圓的直徑與球的直徑,用 $\pi = 3.141592$ 印出圓周長、圓面積、球體積、球表面積,各保留三位小數。公式:周長 $2\pi r$、圓面積 $\pi r^2$、球體積 $\frac{4}{3}\pi r^3$、球表面積 $4\pi r^2$。
1 | 輸入: 10 |
參考解答
1 |
|
這題唯一的陷阱在 4.0 / 3:寫成 4 / 3 就是整數除法,得到 1,球體積會少掉四分之一。const double PI 放在 main 開頭是題目給定值的標準寫法,之後要改成更精確的值只改一個地方。
Q9. 用 Makefile 一次編三題(實驗課第 1 週課堂練習 1-4)
把 Q5、Q6、Q7 分別存成 Q1.cpp、Q2.cpp、Q3.cpp,寫一份模組化的 Makefile:make 要產生三個執行檔,名稱依序是 OddOrEven、IsClassOver、Q3(不是 Q1、Q2,第一週的題目就是這樣指定的),make clean 要把執行檔和 .o 都刪掉。
參考解答
就是〈環境設置〉最後那份「執行檔名稱跟題號不同時」的 Makefile:
1 | CC = g++ |
make 會印六行(三個 -c、三個 -o),ls 應該看到三個執行檔和三個 .o;make clean 之後只剩 .cpp 和 Makefile。交之前一定要 make clean && make 重編一次。
說些什麼吧!