代碼風格的簡單整理

代碼風格

格式

空格

每一行的末尾不應該有空格,不論這行有沒有內容。 每個函數聲明的後面也不應該有空行。

public string getText () {
    string text = search_entry.getText ();
    return text;
}

每個開始括號之前都應該有一個空格:

public string getText () {}
if (a == 5) {
    return 4;
}
for (i=0; i<maximum; i++) {}    //空格不要太多
myFunctionName ();
Object my_instance = new Object ();

數學表達式中的操作數與運算符之間的空格也是必要的。

c = n * 2 + 4;

在變量申明初始化時,也需要通過空格對齊,對齊原則爲數據類型 + N個TAB + 變量名 + N個TAB + = + 初始化值

int             sutdent_num     = 100;
unsigned long max_student_num   = 10000;
char*           student_name    = NULL;

當函數調用或者操作符作爲另一個函數的參數時,可以在小括號前不加空格,如下

char* ptr = (char*) malloc (sizeof(char) * STR_LEN);

縮進

使用4個空格的縮進來保持一致性和可讀性。在 Vim 中可做配置

tabstop=4
softtabstop=4
shiftwidth=4

在類、函數、循環和一般的順序結構中,左大括號跟在第一行的末尾,接着一行是帶了縮進的其他代碼,右大括號則應該在單獨的一行來關閉函數。

public int myFunction (int a, string b, long c, int d, int e) {
    if (a == 5) {
        b = 3;
        c += 2;
        return d;
    }

    return e;
}

在條件語句和循環語句當中,應當總是用大括號包裹住其所屬代碼,即使只有一行代碼

if (my_var > 2) {
    print ("hello\n");
}

同樣的規則適用於 elseelse if

if (a == 4) {
    b = 1;
    print ("Yay");
} else if (a == 3) {
    b = 3;
    print ("Not so good");
}

如果一個變量的值需要判斷兩次以上,使用 switch/case 語句替代多個 else/if 。 if/else 最多兩層嵌套。

switch (week_day) {
   case "Monday":
       message ("Let's work!");
       break;
   case "Tuesday":
   case "Wednesday":
       message ("What about watching a movie?");
       break;
   default:
       message ("You don't have any recommendation.");
       break;
}

行長度

每一行字符數不超過80。但是,下面這些情況可以超過:

  • 如果一行註釋包含了超過80字符的命令或 URL,出於複製粘貼的方便可以超過 80 字符;
  • 包含長路徑的可以超出 80 列,儘量避免;
  • 頭文件保護可以無視該原則。

頭文件保護,一般是在文件第一行,使用宏保護的方式

#ifndef HASHMAP_HEADER
#define HASHMAP_HEADER
...
#endif

非 ASCII 字符

儘量不使用非 ASCII 字符,使用時必須使用 UTF-8 格式。
哪怕是英文,也不應將用戶界面的文本硬編碼到源代碼中,因此非 ASCII 字符要少用。特殊情況下可以適當包含此類字符,如,代碼分析外部數據文件時,可以適當硬編碼數據文件中作爲分隔符的非 ASCII 字符串;更常用的是(不需要本地化的)單元測試代碼可能包含非 ASCII 字符串。此類情況下,應使用 UTF-8 格式,因爲很多工具都可以理解和處理其編碼,十六進制編碼也可以,尤其是在增強可讀性的情況下——如 “\xEF\xBB\xBF” 是 Unicode 的 zero-width no-break space 字符,以 UTF-8 格式包含在源文件中是不可見的。

有時候,使用一個新的 terminal 的時候,編碼使用默認編碼(default),當代碼中有中文註釋的時候,打開查看的時候很可能出現亂碼的情況,最好在編輯源文件的時候,就將編碼設置爲 utf-8 編碼,使用其他工具打開時,通過 utf-8
打開就沒什麼問題。

我的 Vim 編碼配置爲

"file encoding
set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936
set termencoding=utf-8
set encoding=utf-8
set fileencodings=ucs-bom,utf-8,cp936
set fileencoding=utf-8

如果對編碼很感興趣的童鞋,可以閱讀這篇文章 [http://blog.csdn.net/honglicu123/article/details/53932524 ]

函數申明、定義或調用的格式

儘量在同一行,如果一行放不下,可以放在多行

int ret = getMax (a, b);
void goSomeThing (arg1, arg2, arg3,
                  arg4, arg5) {
    /* statements */
}

如果參數很多,處於可讀性考慮每行可只放一個參數。

bool retval = DoSomeThing (argument1,
                           argument2,
                           argument3);

如果函數名太長,以至於超過最大長度,可以將所有參數獨立成行。

布爾表達式

如果一個布爾表達式超過標準行寬(80字符),需要斷行。斷行時,每一個子表達式都需要用括號括起來,同時在新的一行的子表達式的開頭使用 &&,||符號連接

if ((this_one_thing > this_other_thing)
    && (a_third_thing == a_fourth_thing)
    && (yet_another & last_one)) {
  ...
}

在每一行的開頭使用 && 或者 || 將子表達式連接起來,是爲了當需要註釋其中一個子表達式時,通過 /**/ 直接註釋即可

類和文件

每個類只有一個與其對應的文件。

在所有的文件當中,同一個類的名稱應當是一致的。

儘量提取代碼中相同的部分,抽象成類以易於擴展。

註釋

註釋都要在同一行內,即使那一行有代碼。

在同一行中,代碼後面的註釋要縮進。過於顯眼的註釋危害大於好處。

/* User chose number five */
if (a == 5) {
    B = 4;           // Update value of b
    c = 0;           // No need for c to be positive
    l = n * 2 + 4;   // Clear l variable
}

有些人或者公司會硬性的規定註釋必須使用英文,推薦使用英文,但是如果英語表達能力有欠缺,分分鐘說能夠說清楚的,用英文越說越亂,那還是看着辦吧(->_->)

註釋風格

統一使用 /**/

變量註釋

變量註釋,儘量寫在變量申明的同一行

int student_number;     /* student number */

對結構體或者類的成員也這麼註釋

typedef struct NodeFileDescription {
    ElemType* date;             /* 值 */
    struct NodeFileDescription* next;   /* 鏈表指針 */
};

函數註釋

在編寫函數之前,需要爲函數添加相應的註釋,函數名稱,函數功能,參數說明,作者,郵箱,創建時間,修改時間,如下所示

/*************************************************************
 * funcName  :
 * funcDesc  :
 * argsDesc  :
 * author    :
 * email     :
 * createTime: .strftime("%Y-%m-%d %H:%M")
 * modifyTime:
 * ***********************************************************/

我使用的是 Vim 編輯器,所以可以通過鍵盤映射,使用 vimscript 編寫這些功能

:nnoremap <C-m> o/*****************************************************<CR>
            \ * funcName  :<CR>
            \* funcDesc  :<CR>
            \* argsDesc  :<CR>
            \* author    :<CR>
            \* email     :<CR>
            \* createTime:<CR>
            \* modifyTime:<CR>
            \*******************************************************/<ESC>

類的註釋

每個類的定義要附着描述類的功能和用法的註釋。

文件註釋

在每一個文件開頭加入版權公告,然後是文件內容描述。

可查看 redis 中關於 ziplist.c 這個文件的描述,在文件開始,描述了 ziplist 的結構,以及 ziplist entry 的結構。

最後面一般是一些法律和公告信息,還有作者。如果你對其他人創建的文件做了重大修改,將你的信息添加到作者信息裏,這樣當其他人對該文件有疑問時可以知道該聯繫誰。

實現註釋

對於實現代碼中巧妙的、晦澀的、有趣的、重要的地方加以註釋。

TODO註釋

對那些臨時的、短期的解決方案,或已經夠好但並不完美的代碼使用 TODO 註釋。這樣的註釋要使用全大寫的字符串 TODO,後面括號(parentheses)里加上你的名字、郵箱等,還可以加上冒號(colon):目的是可以根據統一的 TODO 格式進行查找:

/* TODO([email protected]): Use a "*" here for concatenation operator. */
/* TODO(Zeke) change this to use relations. */

通用命名規則

函數命名、變量命名、文件命名應具有描述性,不要過度縮寫,類型和變量應該是名詞,函數名可以用 “命令性” 動詞。

儘可能給出描述性名稱,不要節約空間,讓別人很快理解你的代碼更重要。

int num_errors;
int num_completed_connections; 

文件名命名

文件名要全部小寫,可以包含下劃線(_)。可接受的文件命名:

my_useful_class.c
mysql_utils.c

類型命名

類型命名每個單詞以大寫字母開頭,不包含下劃線:MyExcitingClassMyExcitingEnum
所有類型命名——類、結構體、類型定義(typedef)、枚舉——使用相同約定,例如:

class MyClass { ... }             // Class names
struct RedisObject { ... }  // struct name
typedef unsigned int UINT32;    // typedef
enum OperatingSystem { ... }// An enum name is the same as

但是,enum 中的成員,可以推薦使用 enum_ 作爲前綴,聯合體在 uni_ 作爲前綴

變量命名

變量名一律小寫,單詞間以下劃線相連,類的成員變量以下劃線結尾,如 my_exciting_local_variablemy_exciting_member_variable

普通變量或者局部變量:

char* student_name;
char* studentname;

作用域前綴

  • m_ : 類成員 members
  • ms_ : 類靜態成員 static member
  • s_ : 靜態變量 static
  • g_ : 全局變量 global

全局變量:
全局變量儘量少用。如果是指針,前綴爲 g_p**,如果是整型值,使用g_n**

CDataManager* g_pdatamanager;

常量命名

在名稱前加k:kDaysInAWeek。
所有編譯時常量(無論是局部的、全局的還是類中的)和其他變量保持些許區別,k後接大寫字母開頭的單詞:

const int kDaysInAWeek = 7;

函數命名

以小寫字母開頭,往後每個單詞首字母都需大寫。

void getTablEntry ();
void geleteUrl ();

類成員方法命名:

  • 有 1 個或多個單詞組成,第 1 個單詞首字母小寫,往後所有單詞首字母大寫
  • 保護函數 protect,開頭加上一個下劃線,如 _setStat ()
  • 私有函數 private,開頭加上兩個下劃線,如 __destroyImpl ()
  • 虛函數,以 do 開頭,如 doFly ()
  • 接口,以大寫字母 I 開頭

枚舉類型

枚舉屬於類型,命名以大小寫混合的方式,首字母大寫,往後每一個單詞首字母均大寫。枚舉值必須全部大寫,單詞之間通過下劃線連接

typedef enum UrlTableErrors{
    OK = 0,
    ERROR_OUT_OF_MEMORY,
    ERROR_INVALID_INPUT
}UrlTableErrors;

宏命名

宏名稱全部大寫,並且多個單詞通過下劃線連接。
注意:當宏有表達式時,宏定義需要用小括號連接起來

#define MAX_NUM 100000
#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))    // redis中求取壓縮鏈表長度字節數的宏定義

爲使代碼簡潔,可讀性強,好需要尊重一下一些原則。

1、 使用有意義的變量名和函數名。

  • 變量名要能夠知名達義。
  • 局部變量儘量接近使用它的地方
  • 局部變量名字簡短,因爲按照第二個原則,局部變量很接近使用的地方,能夠比較簡單的就能獲知局部變量的含義
  • 不要重用局部變量

2、 函數不要太長,把複雜的邏輯提取出去,做成 “幫助函數”
3、 把複雜的表達式提取出去,做成中間變量,不要將表達式嵌套過深
4、 在合理的地方換行
5、 避免使用類成員或者局部變量傳遞信息,通過返回值的形式傳遞。
6、 避免使用自增或者自減表達式,除非是單獨的語句或者在 for 循環中
7、 永遠不要省略括號,合理使用括號表示表達式的優先級,默認的運算符的優先級有時候會很讓人迷惑,因爲需要記住所有運算符的優先級也是一件頭疼的事情。

8、 避免使用 break 和 continue,解決方法如下:

  • 對 continue,可以將 continue 出現的條件反向,消除
  • 對 break,可以將 break 出現的條件放在循環體條件中,消除
  • 有時可以將 break 換成 return,消除 break
  • 如果以上均不適用,可將循環體中複雜的部分提取出來,做成函數調用,以達到去掉 continue 或者 break 的目的

寫直觀的代碼
代碼接近自然語言描述,不要使用過多的語言特性或者“巧妙”的做法,直接、清晰就好。比如將 if 語句使用 || 或者 && 代替

conditionA && conditionB || conditionC

條件 A 爲 True 時才執行 B,B 爲 True 不執行 C,B 爲 False 時才執行 C,要理解這個語句,需要多轉很多彎,很沒必要這麼寫,直接一點

if (conditionA) {
    if (conditionB) {
        ...
    } else if (conditionC) {
        ...
    }
}

if 語句做多兩層嵌套,如果太多,使用 switch 語句代替

正確處理錯誤
1、 儘量考慮和處理每一個可能出現的錯誤

2、 對每一種 Exception 單獨處理,即 catch 異常時,儘量 catch 具體的 Exception,在捕捉到異常時,及時處理,不要繼續 throws

正確處理 NULL 指針
1、 儘量不要產生 null 指針。不用 NULL 初始化變量,函數不返回 NULL 值。在函數返回結果中,“沒有了”或“出錯了”都不等同於 NULL,Java 中可使用異常

2、 不要把 NULL 放入 “容器數據結構” 中,即 Array、List、Set 等,也不應該出現在 Map 的 Key 和 Value 中

可以用一個特殊的、真真合法的對象表示“沒有了”或者“出錯了”,不要用 NULL 表示,不然往後每次都需要對 NULL 進行檢查處理。
null 不是一個合法的對象,在 lua 中,nil 就是一種數據類型,該數據類型就是 nil,應當這麼來看待 NULL,將它作爲一種特殊的對象來處理

3、 函數調用者,要明確理解 NULL 所表示的意義,不同場景下 NULL 表示的意義不同。同時,儘早檢查和處理 NULL 返回值,減少 NULL 的傳播。不要在檢查到 null 的時候,再返回 null

4、 函數作者,明確申明不接受 NULL 作爲參數,一旦參數是 NULL,將立即出錯,崩潰。

當調用者使用 NULL 作爲參數調用函數時,程序崩潰,調用者需要對程序崩潰負全責。

試圖對 NULL 進行容錯,就會造成,需要在程序無休止的對 NULL 進行檢查和處理,這個讓人特別煩。 一開始就對 NULL 零容忍

參考文獻:

  1. clean code
  2. 王垠:編程的智慧
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章