Skip to content

Latest commit

 

History

History
210 lines (194 loc) · 7.27 KB

程式撰寫風格.md

File metadata and controls

210 lines (194 loc) · 7.27 KB

程式撰寫風格

養成固定的程式撰寫格式和習慣是一件相當重要的事情
初學者往往會忽視程式撰寫風格 (style) 的重要性
因為光是要把程式寫對都沒時間了
哪管得到那麼多 "枝微末節" 的東西
但是經驗告訴我們
在還沒養成壞習慣之前才是最容易導正的時候
而且有了良好的撰寫風格也能避免犯下許多不必要的程式錯誤
所以我們會持續要求大家寫程式的格式
不僅程式碼語法和語意要正確
還要 "言簡意該"
如果有直接了當的表達方式絕對不要拐彎抹角或是賣弄花俏
另外排版也要美觀且一目了然

程式碼語法語意正確是為了要讓電腦能看懂
程式碼簡潔美觀則是要讓人能夠理解程式內容
方便未來做修改或是維護

底下有關程式風格的重點
摘自 "The Practice of Programming" by Kernighan and Pike
推薦大家去找原書來看

命名規則

  1. Use descriptive names for globals, short names for locals. 
  • 全域變數的命名最好包含完整的描述
  • 但是 function 裡面的局部變數的名字則要短一點   clarity is often achieved through brevity
  • 迴圈的 index 通常用 i 和 j
  • 局部的指標變數通常用 p 和 q
  • 字串則常用 s 和 t
       for (i = 0; i< nelems; i++)  
          elem[i] = i;
  • 指標變數的名字可在開頭或結尾加上 p 做區隔, 例如 nodep
  • 全域變數的第一個字母大寫 
  • 常數則全部的字母都大寫
  • 要採用 "固定" 的命名原則, 至於要用 numPending 或 num_pending 的則視個人喜好而定
  1. Be consistent.
  • 不管喜歡採用甚麼樣的命名原則, 重點是要遵守固定的原則
     class UserQueue {
        int front, capacity;
        public int nusers() {...}
    }

使用起來像是

    queue.capacity++;
    n = queue.nusers();
  1. Use active names for functions.
  • function 的作用就是在做某件事情, 所以名字裡最好能帶有動作
    now = date.getTime();
    putchar('\n');
    if (isupper(c)) ...
  1. Be accurate.
  • 變數或 function 的名字要和它們所代表的意義或作用符合 下面的 function 就名不符實 (strcmp(s, t) 會比較兩個字串 s 和 t 誰比較大, 如果一樣大回傳值是 0)
    int smaller(char *s, char *t) {
        if  (strcmp(s, t) < 1)
            return 1;
        else
            return 0;
    }

Expressions and Statements

  1. Indent to show structure.
  • 要有固定的排版方式
  • 通常都會縮排四個字元   
     for (n++; n < 100; n++)        
     field[n] = '\0';    
     *i = '\0';    
     return '\n';
  1. Use the natural form for expressions.
  • 程式的邏輯要直接了當例如   
     if (!(block_id < actblks) || !(block_id >= unblocks))

就太繞舌應該改成   

     if ((block_id >= actblks) || (block_id < unblocks))
  1. Parenthesize to resolve ambiguity.
  • 如果對於運算符號的優先順序有疑惑,最好別偷懶,加上括號以免造成模稜兩可的情況例如底下這個就有問題   
     if (x&MASK == BITS)

依照優先順序,上面的句子會被解讀成   

     if (x & (MASK==BITS))

但是這應該不是程式設計者的本意底下應該才是完整的寫法   

     if ((x&MASK) == BITS)
  1. Break up complex expressions.* 盡量不要寫一長串複雜的句子炫耀自己對於語法的熟練度, 例如:    *x += (*xp=(2*k < (n-m) ? c[k+1] : d[k--]));*講話有道理比耍嘴皮子來得重要
  2. Be clear.* 創意很重要,但不需要用賣弄小聰明的方式來表現* The goal is to write clear code, not clever code.例如   
     child=(!LC&&!RC)?0:(!LC?RC:LC);

其實要做的事情不過是   

     if (LC == 0 && RC == 0)        
     child = 0;    
     else if (LC == 0)        
     child = RC;    
     else        
     child = LC;
  1. Be careful with side effects例如   
     str[i++] = str[i++] = ' ';

不確定 i 甚麼時候會被加一所以最好是直接了當寫出來   

     str[i++] = ' ';    str[i++] = ' ';

另外像是   

     scanf("%d %d", &yr, &profit[yr]);

上面那樣的寫法是錯的因為 "all the arguments to scanf are evaluated before the routine is called"所以應該寫 

     scanf("%d", &yr);    
     scanf("%d", &profit[yr]);

Consistency and Idioms

  1. 要採預一致的縮排以及括號的原則
  2. 善用 idioms (片語, 慣用語法)例如迴圈的標準與法   
     for (i = 0; i < n; i++)        
     array[i] = 1.0;

或   

     for (ap = arr; ap < arr+128; ap++)        
     *ap = 0;

     while ((c = getchar()) != EOF)        
     putchar(c);

另外像是取得記憶體的寫法   

     p = malloc(strlen(buf)+1);    
     strcpy(p, buf);
  1. if ... else if ... else 慣用法* 當有多種可能狀態需要判斷時 (每種狀態不會同時成立),可以用這種語法

Function Macros

function macro 主要是為了提高效率  因為呼叫一般的 function 需要額外的 stack 運算  但是對目前的 compiler 來說,使用 function macro 並不會提高多少效率  反而容易增加寫錯程式的風險  "With modern machines and compilers, the drawbacks of unction macros outweigh their benefits."

  1. Avoid function macros.  
  2. Parenthesize the macro body and arguments.  *如果無法避免,一定要用 function macros,  一定要把參數加上括號,  並且 function 的主體也要加上括號例如底下的寫法有問題   
     #define square(x) (x)*(x)

當你使用1 / square(x)會變成1 / (x) * (x)所以應該是寫成#define square(x) ((x) * (x))

Magic Numbers

  1. Give names to magic numbers.
  2. Define numbers as constants, not macros.* 在 C 裡面可以用 enum 來定義常數,盡量不要用 #define3. Use character constants, not integers.* 依照對應的型別來使用常數
     str = NULL;   
     name[i] = '\0';  
     x = 0.0;
  1. Use the language to calculate the size of an object.   
     char buf[1024];    
     fgets(buf, sizeof(buf), stdin);

底下的例子是不得不用 macros 的情況:   

     #define NELEMS(array) (sizeof(array) /sizeof(array[0]))    double dbuf[100];    
     for (i = 0; i < NELEMS(dbuf); i++)

因為 function 無法在 array 宣告時就算出 array 的大小

Comments

適當的註解很重要,可以幫助自己或別人理解每一段程式碼在做甚麼事情

  1. 顯而易見的程式碼就不需要再加上沒有建設性的註解
  2. Comment functions and global data* 全域變數和 function 最好都加上註解
  3. Don't comment bad code, rewrite it.* 你發現程式碼需要加一堆註解,才能把要做的事情解釋清楚,那就表示程式碼其實需要再改寫
  4. Don't contradict the code.* 註解的作用是進一步說明程式碼,解釋的內容不能和程式碼本身有所牴觸
  5. Clarify, don't confuse.* As much as possible, write code that is easy to understand; the betteryou do this, the fewer comments you need. Good code needs fewer comments than bad code.