0. 關於「原始碼撰寫風格」 作者: Tiberius

C/C++ 的程式原始碼 (以下就簡稱「原始碼」) 並沒有很嚴格的撰寫格式要求。也就是 說,程式碼不必費心編排,電腦就能夠看得懂。

但是,別忘記原始碼是給「人」看的。

先看看自己的程式碼,不妨同時問問自己: 未來要修改這段程式,或者是要把這段程式 交給別人,自己或對方能否馬上抓住這段程式的要旨?也就是說,快速地了解「這段程式 到底有什麼目的」?

程式的原始碼其實就跟文章一樣,光是「編排」就是一門大學問 - 表達同樣的意思, 一篇分段清楚、正確使用標點、用詞淺白的文章,絕對比一篇不分段,讀一句話得讓人倒 抽好幾口氣,處處用艱深字詞的文章容易讓人了解。

有人會很好奇了,「難道程式碼也有用詞、標點、分段的差異?!」其實沒有錯。

在以下的段落中,我便會一項一項的說明「我的」原始碼撰寫風格,以及它們所代表的 意義(如果有的話)。如果我沒有說明它們代表的特殊意義,請不要急著懷疑 - 記得,不管 是什麼撰寫風格,至少都有一個相同的目的,那就是「減輕往後閱讀的負擔」。

預祝各位能夠養成良好的程式碼撰寫習慣!

1. 空白的使用

有些地方該用空白,但是有些地方不該用空白!以下舉一些我覺得好的程式實例來說明:

a = 3 + 4 / 5;             // 在運算符號,還有每個運算元 (變數,數字) 旁邊用一個空白隔開
resp == YES; abc != 3;     // 邏輯判斷符號 (等於,不等於,或) 也用一個空白隔開
while (i == 1); do { ... } while (i == 1); // 控制結構後面的條件判斷式、大括弧,也用一個空白隔開
int swap(char *a, char *b) // 函式傳回值宣告之後的一個空白,還有參數列表中逗號後的空白,以及參數型態宣告後的空白。

以下是一些我覺得不好的空白舉例 (下一行則是建議的寫法):

for (i = 3; i <= 10; i ++ ) // for 迴圈中的條件判斷我當特例處理。
for (i=3; i<=10; i++)       // 因為這裡常需要寫得很複雜,所以不要加空白增加閱讀困難
int main ();                // 函式名稱與參數列表中不要加空白
int main();

2. 大括弧、內縮的使用 (段落編排)

首先,每層內縮都應該縮進 4 個空白。你可以修改編輯器的設定,讓 TAB 鍵插入 4 個空白。

接著,程式碼應該以這樣的方式使用括號與內縮:

int main() {
    ....
    if (something == 1) {
        ....
    } 
    else if (something == 2) {
        ....
    }
    else {
        ....
    }
}

注意到了嗎?左大括號跟著上一行,而右大括號自成一行。而且就算大括號括起來的範圍只有一行,也不寫成這個樣子:

if (something == 1) return;
if (something == 1)
    return;

這兩種寫法雖然省空間,但是第一種寫法在之後會比較難找到後面的那個敘述 (尤其是條件判斷非常長的時候),而 第二種寫法要加入新敘述時還必須手動補上括號。最重要的是「這兩種長得與其他段不一樣」。

如果 if 的判斷非常長,可以依照下面的寫法加以斷行:

if ((something_one && something_two) || (other_one && other_two) ||
    (elsething_one && elsething_two)) {
    ...
}

也就是把邏輯符號 (&&, ||) 留在行尾,下一行內縮進來對齊。注意大括號還是跟在後面。

3. 變數的宣告與使用

幾個要點,第一個是「指標的 * 應該跟在變數名稱上」

char* a, b, c;   // 錯誤! 只有 a 會是指標 (雖然要是只宣告一個的話就沒差)
char *a, *b, *c; // 正確寫法

第二個,使用有意義的英文名稱,一看名字就知道這個變數幹什麼用的。
區域變數用「首字小寫,第二個字大寫」的方式,全域變數或常數則用「全大寫,底線區別單字」的方式方便閱讀。

long maxScore;    // 留意 m 小寫, S 大寫, 建議用法
long curScoreAvg; // 現在分數的平均 (current score average),多清楚呀 (後面不要用到其他用途上 ...)
long MAX_SCORE;   // 全域變數建議用法

long a, b, c;     // 幹什麼用的?
long temp, tempb; // 暫存什麼東西?
long Maxscore;    // 區域還是全域變數?

4. 註解

註解跟程式原始碼有著同等重要的地位。在一個大程式中,註解有可能佔了 1/4 至 1/3 甚至更多的原始碼空間。 理由很簡單,多留一點當初的想法、工作流程的說明,在未來重新閱讀的時候可以更快了解程式碼的作用。

註解其實也有自己的一套準則可以遵守。首先是行內註解:

// 增加 X
x += 1;

// 預備處理下一筆資料
x += 1;
// 讀取輸入檔
...
// 計算標準差
...
// 輸出統計圖表
...

同樣是行內註解,第一個加了不如不加 - 因為根本沒有實質意義。相較之下第二個就敘述得比較完備了。

接下來是說明一個函式的註解,這種註解的行數通常很多,因此使用區塊註解較為合適。留意註解中所提供的資訊:

/***********************************************************************
 *  CalcuateAverage
 *
 *  作者: Tiberius (2002/05/29)
 * 
 *  計算學生的加權各科總平均成績。
 * 
 *  輸入: people_t *student - 指向 "學生成績資料" 陣列的指標
 *        weight_t *weight  - 指向 "各科加權表" 陣列的指標
 *  輸出: 加權平均成績
 ***********************************************************************/
double CalcuateAverage(people_t *student, weight_t *weight) {
    ....
}

詳盡的程度要到「說明書」的程度 - 別人看了就會用,而且不會用錯才行。
這部分的重要性,甚至有人已經寫了程式,可以把原始碼中的這個部分抓出來自動做成 輔助說明檔!雖然要用這個功能,這部分要用一些特殊的寫法,但是現在可以不管這個 部分,只要「讓人可以輕鬆看懂」就行。

一般來說,在檔案的開頭也要加上同樣類型的註解,說明一下這個檔案裡頭提供了 哪些功能 (函式, 物件等) 以及使用上需要留意的地方。如此一來只要把原始碼內的 註解整個閱讀過一遍,就能對程式本身有通盤的了解。

XXX 待補完