回目錄

第二章 變數與指定運算子「=」

原 Google Sites 連結:/cppzero/di-er-zhang-bian-shu-yu-zhi-ding-yun-suan-zi

目錄

2.1 變數與輸入運算子

前面已經做了兩個題目,都是輸出一個固定的字串。但是這樣的程式好像沒有太大的用處,現實生活中,大部份的程式都需要輸入一些資料,再根據所輸入的資料來做一些處理然後再輸出所得到的資訊。接下來我們就來看第一個需要輸入資料的題目:

d049. 中華民國萬歲!

這個題目要求我們輸入一個西元年份,把它轉成民國年份後輸出。這題很簡單,因為大家都知道只要把西元年份減掉 1911 就會變成民國年份了。問題是在 C++ 的程式中要如何去輸入呢?

C++ 中的輸出指令是 <<,輸出的標的如果是螢幕,就用 cout。而 C++ 中的輸入指令則是 >>,輸入的來源如果是鍵盤,就用 cin (代表 console input,唸成 see-in)。因此,在 C++ 中要輸入資料可以寫成以下陳述式:

cin >> 變數;

這個陳述式所代表的動作為「從鍵盤輸入資料並存入指定的變數中」。在執行這個指令之前,我們要先定義一個變數來接收從鍵盤所接收到的資料。定義變數的語法如下:

資料型態 變數名稱;

比如說,如果你要定義一個名稱為 y 的整數變數,你可以寫成:

int y;

其中的 int 為型態名稱,y 則是所定義的變數名稱。當我們在程式中定義一個 int 變數時,VC++ 會為這個變數保留 4 個 Byte 來它儲存這個數字。C++ 定義了許多不同的資料型態,int 是其中的一種,這個型態的變數可以儲存一個介於 (含) -2147483648 ~ 2147483647 之間的整數。

其實除了 int 以外,C++ 還定義了很多其它的「基礎資料型態」。
坊間的電腦書多是在第一章或第二章就開始介紹各種資料型態,但是沒寫過程式的初學者很難了解這些概念。
因此,我們把「基礎資料型態」留到第五章再仔細地討論,第四章以前所有的題目都只需要 int 變數就夠了。

在數學上,我們習慣使用一個字母的變數名稱,但是在寫程式時,我們卻希望變數名稱可以望文生義、不解自明。例如上面的例子我們定義了一個名稱為 y 的變數,看程式的人大概很難從它的名稱去猜到這個變數所代表的意義。但是如果你把變數名稱定義為 year,那麼每個人都可以知道這個變數就是用來儲存年份的。

C++ 允許你用較長的變數名稱來增加程式的可讀性,但是變數名稱的訂定也有它的規則:

  1. 變數名稱僅能使用大小寫英文字母「A~Z」,「a~z」,阿拉伯數字「0~9」,以及底線「_」。(底線在鍵盤上位於「-」 的上面,要搭配「shift」來使用)。其他的符號 (包含空白) 都不可以出現在變數名稱之中。
  2. 變數名稱不能以阿拉伯數字「0~9」為開頭的第一個字母。雖然變數名稱可以用底線「_」開頭,可是由於系統變數多是以「_」作為第一個字母,我們應讓避免也使用這樣的變數名稱,以免混淆。
  3. 大小寫不同時視為不同的變數。不過習慣上我們通常會用小寫字母作為變數名稱,大寫字母來作為常數名稱。
  4. 不能用「保留字」作為變數名稱。所謂的「保留字」是系統保留下來作為特殊用途的字,不能再拿來作為變數名稱。目前我們的稱程式中所用到的「using」、「namespace」、「int」等就是保留字。VC++ 會把程式中的保留字改成藍色字體顯示,這個功能可以幫助我們辨識我們的變數名稱是否用到了保留字。

下表則是一些合法及不合法的變數名稱:
(表格)

不過由於本課程是以解題為主軸,而這些題目上如果已經定義了一些變數的名稱,我們倒是不妨予以沿用。一來這些變數名稱在題目中已經定義好了,會去看你的程式的人通常也都已經先看過題目了,所這些變數名稱不是沒有意義的。二來這些變數名稱比較短,可以讓你的程式看起來「簡潔」些。這題的「輸入說明」中已經把輸入的整數定義為 y,代表一個西元年份,所以我們的程式中的變數不妨也沿用這個名稱。

有些解題的程式師喜歡把程式寫得很短,有的線上裁判甚至會公布你的程式碼的長度,於是就有人想盡辦法來縮短程式碼,以炫耀自己的功力。以軟體工程的角度來看,這是很要不得的,因為程式的可讀性遠比它的長度來得重要。筆者還是鼓勵大家用有意義的字來作為變數名稱。就算要使用較短的變數名稱,好歹也要用有意義的文字的縮寫。以下面的等加速度物體的位移公式為例:

d=v0t+12at2 d = v_0 t + \tfrac{1}{2} a t^2

其中的變數名稱雖然都只有一個字母,但卻都是有意義的:

ddisplacement (位移)
v0velocity at time 0 (時間 0 時的速度,初速度)
ttime (時間)
aacceleration (加速度)

定義好了變數,也用 >> 從 cin 輸入了一個值並存到變數 y 裡,接下來我們就可以用 y 來做運算,並把結果顯示出來,所得的程式如下:

#include <iostream>
using namespace std;

int main () {
    int y;
    cin >> y;
    cout << y - 1911;
}

電腦在執行你的程式時,是按照順序一行一行往下執行的,所以上面這個程式的三個陳述式出現的順序不可以隨意調換。一定要先用 int y; 定義一個變數 y,接下來才會有一個變數 y 可以讓 cin >> y; 來輸入資料;一定要先輸入一個西元年份 y,才能根據 y 來計算民國年份。

程式寫好了,按 F5 開始執行。這時候出現了一個黑底的 DOS 視窗,但視窗上卻沒有任何的顯示。這時候請按一下工具列的

通常使用者在執行程式時,如果看到一個黑色的畫面卻沒有任何的顯示,由於他們不知道程式正在等他們輸入,所以他們很可能會以為程式當掉了。為了避免這樣的誤會,一般的程式設計書籍會建議在讓使用者輸入資料前,先輸出一些提示,如下:

#include <iostream>
using namespace std;

int main () {
    int y;
    cout << "Please enter a year: ";
    cin >> y;
    cout << y - 1911;
}

如此一來,程式執行時就會先顯示「Please enter a year:」,使用者看到時便會知道要輸入一個年份 (如果他英文不是太爛的話)。不過這樣的做法在解題時卻反而會造成一些問題,因為我們的程式不是要給一般的使用者來執行的,而是要上傳給線上裁判來執行。線上裁判不需要任何提示,只要程式一開始執行,它就會自動依題目中「輸入說明」的規定開始輸入資料。而程式中輸出到 cout 的任何資料卻都會被視為你的答案的一部份,甚至包括一開始的「Please enter a year:」。由於你的答案比預存在伺服器上的標準答案多出了一些文字,比對的結果當然也就不一樣,你的程式也會因此而得到 WA。因此,在寫解題用的程式時,除了在「輸出說明」所要求的輸出以外,千萬不要再畫蛇添足地加一些文字,因為那會影響答案的判斷。

現在你知道如何輸入一個值到變數裡,然後再利用那個變數去計算題目所要求的結果了。現在讓我們來看看這一題:

d461. 班際籃球賽

這個題目看起來很複雜,其實它非常簡單。不管賽程如何安排,每一場比賽都會淘汰一個隊伍,所以如果有 n 個隊伍,那麼就要舉行 n - 1 場比賽才能產生最後的冠軍。簡單嗎?趕快去增加你的 AC 題數吧!

再練習一題:

d063. 0 與 1

題目大意:輸入的值只有兩種可能:0 與 1,請你輸出與輸入「相反」的數字,也就是輸入如果是 0,請你輸出 1;輸入如果是 1,請你輸出 0。

假設所輸入的值儲存於變數 x 中,你可以利用之前所教的「算術運算子」算出與 x「相反」的值嗎?

這題可以有很多種不同的寫法,你不妨先拿出一張紙來,不要看下面的答案,自己試著想一想,看看你的方法和筆者一不一樣。

cout << 1 - x;

直接用 1 去減掉 x 就好了。

2.1.1 整數除法

在 C++ 所定義的 5 個「算術運算子」中,「/」(除) 是要比較小心的一個,因為它所執行的是「整數除法」。如果你執行:

cout << 11 / 4;

所顯示的結果不會是 2.75,而是 2。

筆者戲稱它為「小三除法」(是「小三」,請你不要倒過來唸!),因為小學三年級的小朋友沒有學過小數,如果你要他算 11 除以 4,他只會算到整數位,然後告訴你「2 餘 3」。也就是說,他會給你兩個值,一個是「商」、一個是「餘數」。但是在程式語言中,一個運算子只能回傳一個值,於是 C++ 便分別為「商」和「餘數」各定義了一個運算子:「/」傳回商,「%」傳回餘數。因此,

cout << 11 / 4;

會顯示 2,而

cout << 11 % 4;

會顯示 3。

d063. 0 與 1

在上一節中,我們用減法很簡單地就解決了問題。但是那並不是唯一的解法,你也可以用餘數來解這一題:

cout << (x + 1) % 2;

先加 1 再除以 2 求餘數。如果 x 是 0,加 1 後就變成 1 了,但是如果 x 是 1,加 1 以後雖然變成了 2,但是再除以 2 求餘數就變成 0 了。

那如果你同時需要商及餘數時怎麼辦?這時候你只能用「/」及「%」各算一次了。下面這題便是一個例子:

d827. 買鉛筆

題目大意:一支鉛筆 5 元,一打鉛筆 50 元。n 支鉛筆多少錢?

你可以用「/」求出要買幾打、用「%」求出要零買幾支,再依價錢算出總價即可。

cout << (n / 12) * 50 + (n % 12) * 5;

2.1.2 無條件進位

由於「/」只算到整數位,之後的便捨去不再計算,因此,這樣的商可以視為是「無條件捨去」之後的結果。可是如果要「無條件進位」時怎麼辦?

d073. 分組報告

題目大意:從 1 號開始,每 3 個人分成一組。給你某人的編號 n,請問他編在第幾組?

既然是每三個人一組,當然會用到 / 3 的運算。但是如果你直接寫:

cout << n / 3;

這個寫法只有在 n 是 3 的倍數時才會有正確的結果,其他的情況所求出的組別會比實際的組別少 1。比如說,1 號和 2 號除以 3 會得到 0。

從另一個角度來看,「/」是無條件捨去,但是這題是要無條件進位。在執行整數的除法運算時,如果要無條件進位,只要在除以前先加上比除數還小 1 的值即可。比如說,這題的除數是 3,那麼就在除法運算之前先加上 2 就可以了:

cout << (n + 2) / 3;

接下來再給你兩個練習題。在做這兩題時,提醒你要善用 % (餘數) 運算子。

d050. 妳那裡現在幾點了?

d060. 還要等多久啊?

2.2 多個變數的輸入

我們再看一題:

d485. 我愛偶數

這個題目要求我們計算 a 與 b 之間一共有幾個偶數。在計算之前,我們必須先定義並輸入 a 及 b 這兩個變數。首先,定義兩個變數你可以這麼寫:

cin >> a;
cin >> b;

但是如果你要定義的兩個變數的型態是相同的,(在這個兩個變數都是 int 型態),你可以把它們寫在同一個陳述式裡:

int a, b;

兩個變數之間要以一個逗號隔開來。

接下來要輸入這兩個變數的值,我們可以這樣寫:

cin >> a;
cin >> b;

題目中的「輸入說明」部份提到所輸入的這兩個變數會用空白隔開來。其實 >> 可以讀入以任何「白空白」(white space) 隔開的數字,也就是你在輸入這兩個數字時,把這兩個數字打在同一行,用空白隔開來;或是兩個數字各自打在一行都沒有關係,因為「空白」與「換行」都是「白空白」。

(圖一) (圖二)

如果兩個數字是用白空白以外的字元隔開的話,那就比較麻煩了。如果我們輸入「1,2」,第一個陳述式 cin >> a; 會把 1 讀到 a 裡,第二個陳述式 cin >> b; 卻讀到了「,2」,C++ 並沒有辦法正確地把 「,2」轉成整數,所以 b 就沒有辦法得到正確的值了。還好,絕大部份的題目的輸入資料都是用空白來隔開數字,所以不用太擔心這個問題。

當我們有兩個、或兩個以上的變數需要輸入時,其實也可以把它寫成一行,(不同型態也沒有關係):

cin >> a >> b;

進階閱讀

為什麼可以寫成這樣呢?這個你得耐心聽我講。

之前當我們講到 >> 時,我們說它是「輸入指令」, 則是「輸出指令」。其實用更確切的方式來說,它們是運算子: >> 是「輸入 (擷取) 運算子」(Extraction Operator);

傳統的算術運算子在執行過後會傳回一些我們所要的資訊。比如說 + 運算子會傳回兩數的和,- 運算子會傳回兩數的差…… 等。如果你執行 123 + 456 的運算,它就會產生 579 的結果回傳給你。但是實在看不出來陳述式 cin >> a; 中 >> 這個運算子倒底做了什麼「運算」?的確,並不是每 C++ 中的運算子都會透過計算產生一些計算來產生新的資訊回傳給你,在使用這些運算子時,它們的「副作用」(Side Effect) 要遠比它們的回傳值還來得重要。

所謂的「副作用」就是在執行運算時對程式中的變數或執行環境產生了一些改變。以陳述式 cin >> a; 為例,它的副作用就是從鍵盤輸入一個整數,並存入變數 a 之中。這個運算執行完畢時,a 的值就會被改變,這就是 >> 運算子的「副作用」。

相對地,算術運算子卻是完全沒有「副作用」運算子。當你執行 a + b 的運算時,它會產生 a 與 b 的和並回傳給你,但是運算執行完後,變數 a 與 b 本身的值並沒有任何改變。

就像算術運算子會有回傳的值一樣,其實每一個運算子都會有一個回傳的值。可是 >> 和 運算子倒底會回傳什麼東西呢?

>> 和 << 運算子在「運算」完畢之後,會回傳該運算子左側的「物件」。以陳述式 cin >> a; 為例,>> 左側的物件就是 cin 本身,所以它回傳的「值」也是 cin。C++ 語言會自動忽略每個陳述式最後所回傳的值,所以執行陳述式 cin >> a; 之後所回傳的 cin 也被 C++ 所忽略,所以我們平常並不太在意 >> 運算子回傳的「值」是什麼。

但是當你在一個陳述式中使用好幾個 >> 運算子時,它的回傳值就很值得玩味了。

在陳述式 cin >> a >> b; 中,我們用了兩個 >> 運算子。因為 >> 運算子的結合性為由左至右,因此左邊的那個 >> 運算子會先「算」,算完以後會回傳 cin,那麼下一個 >> 運算子的運算就變成 cin >> b; 了。

為了讓你有個更清楚的概念,我們先用算術運算子的例子來讓你了解「回傳值」的應用。以下為運算式 1 + 2 * 3 的求值過程:

1 + 2 * 3     因為 * 的優先順序為 4,+ 的優先順序為 5,所以 * 先算。
1 + (2 * 3)   2 * 3 的回傳值回 6,所以用 6 來取代 (2 * 3)。
1 + 6         1 + 6 的回傳值為 7,所以用 7 來取代 1 + 6。
7             最後的回傳值為 7。

接下來我們來看看運算式 cin >> a >> b 的求值過程。

cin >> a >> b     >> 的結合性為由左至右,所以左邊的 >> 先算。
(cin >> a) >> b   cin >> a 的回傳值為 cin (副作用:輸入 a 值),所以用 cin 來取代 (cin >> a)
cin >> b          cin >> b 的回傳值為 cin (副作用:輸入 b 值),所以用 cin 來取代 cin >> b
cin               最後的回傳值為 cin。

因為 C++ 會自動忽略陳述式的最後一個回傳值,所以陳述式 cin >> a >> b; 最後的回傳值 cin 被忽略掉了。但是這類運算子重要的是它們的「副作用」,雖然最後的回傳值被忽略了,但是在求值的過程中,變數 a 和 b 的值卻都已經輸入完畢。

由於 C++ 會自動忽略最後一個回傳值,所以一個陳述式的最後一個執行的運算子必須是有「副作用」的,否則那個運算子就沒有意義了。比如說,下面這個陳述式是完全合法的,你也可以把它編譯後執行,但是這個陳述式卻沒有任何意義,因為它辛苦求得的最後回傳值 106638 被 C++ 給忽略了。

123 + (456 - 321) * 789;

但是如果把它改成:

cout << 123 + (456 - 321) * 789;

這陳述式中最後一個執行的運算為 cout << 106638,其回傳值為 cout,雖然它被 C++ 給忽略了,但是運算式 123 + (456 - 321) * 789 的結果卻已經透過 << 的「副作用」顯示在螢幕上了。

下面這個程式可以輸入兩個整數的值 a, b (b ≥ a),然後輸出它們的差:

#include <iostream>
using namespace std;

int main () {
    int a, b;
    cin >> a >> b;
    cout << b - a;
}

但是「d485. 我愛偶數」這題不是要你求它們的差,而是要你求它們之間有幾個偶數。很難嗎?沒有關係,筆者給你一些提示:≥ a 的最小偶數為 a + a%2;≤ b 的最大偶數為 b - b%2。這樣你會算了嗎?

小提醒:要注意運算子的順序,必要時要加括號。

2.3 指定運算子

d489. 伏林的三角地

給你三角形的三邊長,要你求出這個三角形的面積的平方。

講到三角形的面積,你一定會想到「海龍公式」。

T=s(sa)(sb)(sc),s=a+b+c2 T = \sqrt{s(s-a)(s-b)(s-c)}, s = \frac{a+b+c}{2}

只是因為我們求的是面積的平方,所以「海龍公式」最後一個開平方的動作就可以不用作了。可是要把這個公式寫成程式時,卻產生了一個問題:這個公式是二段式的,我們得先求出 s (週長的一半),再把 s 代入公式的第二段。可是到目前為止,我們所使用的 cout << 輸出方式好像只能一次就把答案求出來,不能先產 s 這個中間產物。當然,我們也可以把 s 直接代入第二段寫成以下陳述式:

cout << (a + b + c) / 2 * ((a + b + c) / 2 - a) * ((a + b + c) / 2 - b) * ((a + b + c) / 2 - c);

我們也可以用數學的觀念將這個陳述式簡化為:

cout << (a + b + c) * (-a + b + c) * (a - b + c) * (a + b - c) / 16;

但是它還是比原來的海龍公式還要長且複雜,電腦要花較長的時間來計算這個公式,而且沒有幾個人看得出來這是海龍公式。

要達到海龍公式原始的二段式模式,我們需要用到「指定運算子」(Assignment Operator),也就是 = (等號)。和大多數的算術運算子一樣,「=」是一個二元運算子,也就是說,等號的前後都需要有一個運算元。其語法為:

v = E
v:變數
E:運算式

在等號的左邊必須擺一個變數,把 = 右邊的值指定給 = 左邊的變數。

和 > 運算子一樣,這個運算子的「副作用」比它的回傳值還重要。

s = (a + b + c) / 2;

參考一下附錄 B 的運算子優先順序,/ 的優先順序為 4,= 的優先順序為 15,所以 / 會先算,所得的回傳值會由 = 運算子指定給 s 作為它的值。當這個陳述式執行完畢時,螢幕上並不會有任何的顯示,但是 s 會得到一個新的值,這就是 = 運算子的「副作用」。

一旦 s 變數內已經有了週長的一半,接下來我們就可以把它代入第二階段的海龍公式並把結果輸出了。

cout << s * (s-a) * (s-b) * (s-c);

題目中有說明三邊長 a, b, c 均為整數,即使如此,所求得的 s 卻可能有小數。不過題目也說明了,答案一定是整數,可是如果 s 有小數且 a, b, c 均是整數,答案就不可能是整數。因此,題目中所給的資料所求出的 s 也不可能有小數,你可以放心地把 s 定義為整數變數。

左值

L-value

2.4 複合指定運算子

d490. 我也愛偶數

要計算一個整數區間 (a 與 b 之間) 的偶數和,我們可以利用梯形公式。如果 a 與 b 本身都是偶數,這題就簡單了:

上底 → a
下底 → b
高 → (b - a) / 2 + 1
面積 → (a + b) * ((b - a) / 2 + 1) / 2

但是因為 a 和 b 不一定是偶數,所以我們得額外再做些處理。之前我們在解「d485. 我愛偶數」時,用的就是上面「高」的公式。在「高」的公式中,a 和 b 都只出現一次,我們可以直接用 a + a%2 來取代公式中的 a、用 b - b%2 取代公式中的 b 就行了。但是這次我們用的是「面積」的公式,其中 a 和 b 各出現 2 次,如果我們還是直接代進去,一來公式變很長,二來額外的運算也會浪費 CPU 的時間。

即然我們學過了 = 運算子,我們可以用之前的二段式處理:

a’ = a + a%2
b’ = b - b%2

然後再用 a’及 b’ 去代上面的梯形面積公式。

但是之前我們說過,變數名稱中只能有英文字母、數字、及底線。因此 a’及 b’ 並不是合法的 C++ 變數名稱。如果你要的話,可以用 a1 及 b1 來代替 a’及 b’ 。

a1 = a + a%2;
b1 = b - b%2;
cout << (a1 + b1) * ((b1 - a1) / 2 + 1) / 2;

你只要把上面的這段程式套入程式的「殼」裡,並定義及輸入所需要的變數,這題就可以 AC 了。不過筆者在這裡要更進一步地討論指定運算字的一些特性與運用。

我們先仔細看一下 a1 = a + a%2; 這個陳述式。在這個陳述式中一共有 =, +, 及 % 三個運算子,根據運算子的優先順序,它們執行的順序為先算 %,再算 +,最後再算 =。

假設 a 等於 5,那麼 a + a%2 就會等於 6,仔細觀察一下上面的程式,當電腦算完這個部份的運算之後,a 變數的值就再也沒有用到了,所以 a 內容也沒有繼續保留的必要。這時候程式大可以把所求得的 6 直接回存到 a 變數裡,而不需要再另外定義一個 a1 變數。於是這個陳述式就變成了:

a = a + a%2;

這樣的寫法對很多初學者而言是難以接受的,因為他們會把它和數學上的表示法互相混淆。以數學的角度來看上面的式子,你會說 a 一定是偶數,否則它不成立。但是這個 = 運算子並不是數學上用在等式中的那個等號,而是用來指定一個值給它左側的變數。我們來看另一個更極端的式子:

a = a + 1;

從數學的角度來看,這個式子根本就是無解。可是在 C++ 裡,這個陳述式卻代表把 a 變數的值加 1。如果 a 原來是 5,執行後就會變 6;如果 a 原來是 6,執行後就會變 7。

這樣的運算式在電腦中很常見,我們通常稱之為「累加」。除了「累加」以外,如果搭配 * 運算子的話,就成了「累乘」了,而這類的運算我們就統稱為「累算」。由於這類的運算很常見,所以 C++ 就提供了更簡潔、更有效率的「複合指定運算子」(Compound Assignment Operators)。

a = a + 1;   可以寫成 a += 1;
a = a + a%2; 可以寫成 a += a%2;
a = a * 10;  可以寫成 a *= 10;
               :
               :
               :

你可以在附錄 B 的運算子優先順序表中找到所謂的「複合指定運算子」。不過使用時要注意,和 = 運算字結合的運算子必須是最後一個執行的運算,例如:

a = a * 10 + 1; 就不可以寫成 a *= 10 + 1;

因為 a *= 10 + 1; 的效果其實等於 a = a * (10 + 1);

如果 a 原來等於 5,執行 a = a * 10 + 1; 之後 a 會等於 51;可是執行 a *= 10 + 1; 之後 a 卻變成 55 了。

整合以上的討論,本題的程式如下:

#include <iostream>
using namespace std;

int main () {
    int a, b;
    cin >> a >> b;
    a += a % 2;
    b -= b % 2;
    cout << (a + b) * ((b - a) / 2 + 1) / 2;
}

註解

把一個較複雜的程式分成幾段來寫也比較容易讓人了解。分段的方式也方便我們為程式加上「註解」。在 C++ 裡註解是以 // (兩個斜線) 開始,至該行的結尾結束。以「d490. 我也愛偶數」的程式為例,我們為它加上註解如下:

//d490. 我也愛偶數 by Snail
#include <iostream>
using namespace std;

int main () {
   int a, b;
   cin >> a >> b;
   a += a % 2;      // 大於等於 a 的最小偶數
   b -= b % 2;      // 小於等於 b 的最大偶數
   cout << (a + b) * ((b - a) / 2 + 1) / 2;
}

當程式在編譯時,所有介於 // 與行尾之間的文字都會被編譯器所忽略,因此它不會對程式的執行產生任何影響。註解的主要目的是為了讓程式比較容易看懂。很多程式師懶得為自己的程式寫註解,結果幾年後連他自己都看不懂自己的程式,因此程式師們應該養成寫註解的良好習慣。

進階閱讀

C++ 有兩種註解的方式:「單行註解」及「多行註解」。上面所介紹的是單行註解,這種註解的方式是 C++ 才有的新方式,C 語言只有多行註解可用。多行註解以 /* 作為開始,以 */ 作為結束,中間可以打很多行的註解。上面的程式改成多行的註解方式如下:

/*d490. 我也愛偶數 by Snail*/
#include <iostream>
using namespace std;

int main () {
   int a, b;
   cin >> a >> b;
   a += a % 2;      /* 大於等於 a 的最小偶數 */
   b -= b % 2;      /* 小於等於 b 的最大偶數 */
   cout << (a + b) * ((b - a) / 2 + 1) / 2;
}

多行註解有很多的問題。首先,如果第 8 行的結尾你忘了打 */ (如下),那麼第 9 行整行都會被視為註解,其中的 b -= b % 2; 陳述式也不會執行了。

/*d490. 我也愛偶數 by Snail*/
#include <iostream>
using namespace std;

int main () {
   int a, b;
   cin >> a >> b;
   a += a % 2;      /* 大於等於 a 的最小偶數
   b -= b % 2;      /* 小於等於 b 的最大偶數 */
   cout << (a + b) * ((b - a) / 2 + 1) / 2;
}

像這樣的錯誤並不會被編譯器偵測到,程式可以順利執行,但是結果卻不對,很難去抓錯。

其次,有時候我們會想要取消一部份程式碼的作用,但是又不想要把它刪掉,我們會把它們變成「註解」。這時候你可能會認為多行的註解比較好用,因為你要在要註解掉的程式碼的前後分別加上一個 /**/ 就可以了,哪怕是幾百行的程式也是一次 OK!不過這是很危險的一件事。以上面這個程式為例,把第 8 行及第 9 行註解掉以後程式如下:

/*d490. 我也愛偶數 by Snail*/
#include <iostream>
using namespace std;

int main () {
   int a, b;
   cin >> a >> b;
/*
   a += a % 2;      /* 大於等於 a 的最小偶數 */
   b -= b % 2;      /* 小於等於 b 的最大偶數 */
*/
   cout << (a + b) * ((b - a) / 2 + 1) / 2;
}

要留意多行註解是不能套疊的,第 8 行的 /* 開始了註解,可是到了第 9 行的行尾看到了 */,以為註解已經結束了,所以便開始執行第 10 行的程式。

因此我們不建議使用多行的註解,(可憐的 C 程式師只有多行註解可用)。

其實現的編譯器提供的很強大的編輯功能。以 C++ 為例,如果你要把一大段的程式碼註解掉,你只要把那段程式碼反白,再按工具列上的