Minus-C 一個(gè)最小化的C語(yǔ)言規(guī)范資深C++程序員都不會(huì)對(duì)C++編程規(guī)范太陌生,C++實(shí)在太復(fù)雜,以至于所有項(xiàng)目都需要裁剪一個(gè)子集共項(xiàng)目組內(nèi)使用。經(jīng)過(guò)在家休息這一小段時(shí)間,我發(fā)現(xiàn)其實(shí)C語(yǔ)言更需要一個(gè)相同的規(guī)范,這就是本文的目標(biāo),最大可能規(guī)避C語(yǔ)言的黑暗面。 這里說(shuō)的“不支持、不使用”,是指在沒(méi)有明確要求的程序中,不主動(dòng)使用。但在特殊場(chǎng)景下(如調(diào)用外部接口等),有些黑暗的角落我們還是要去了解。 大體上分成這幾個(gè)部分,這些有些還是只是一個(gè)初步的想法,逐步會(huì)進(jìn)一步擴(kuò)充和裁剪。 1.語(yǔ)言標(biāo)準(zhǔn)和編譯器 如果我說(shuō)出建議使用C99和GCC是不是有很多人長(zhǎng)嘆一口氣,不過(guò)C99事實(shí)只用到了很少一點(diǎn)特性,大部分編譯器都支持,比如說(shuō)//注釋。選擇GCC是因?yàn)樗鼜V范的可獲得性和一致性,而主要以GCC做為一個(gè)驗(yàn)證標(biāo)準(zhǔn)來(lái)看待。 2.文件格式 這部分只要求兩點(diǎn),一是包含基準(zhǔn)頭文件,stdc99.h。C語(yǔ)言的頭文件實(shí)在不標(biāo)準(zhǔn)。二是使用前注釋,而不是行注釋和后注釋。 3.數(shù)據(jù)類型選擇 只選用char, unsigned char, int 這幾個(gè)基本類型,64位系統(tǒng)程序中應(yīng)該還要增加一個(gè)long long 。 不使用typedef的結(jié)構(gòu)體和指針,即結(jié)構(gòu)體都有前綴struct 。 不使用共用體。 不使用enum類型,但使用enum來(lái)定義常量。 數(shù)組類型,需要要特別說(shuō)明,不強(qiáng)制使用。 只在有明確要求的地方使用double類型,小心不精確的表示。 4.函數(shù)使用 一般不使用malloc,以靜態(tài)分配為主,必須進(jìn)行動(dòng)態(tài)內(nèi)存管理時(shí),必須使用這四個(gè)一組的函數(shù)完成。 xxxalloc/xxxsize/xxxfree/xxxextend 不使用scanf/printf這種內(nèi)嵌的語(yǔ)言。 不使用(...)式的可變參數(shù),除非可變參數(shù)是同一類型。 5.表達(dá)式 接受使用+,-,*,/ 除提領(lǐng)‘*’外,不接受++與其它運(yùn)算符同時(shí)出現(xiàn)。 不使用&,|,^,~,<<,>>,見(jiàn)后面“位操作” 不使用?: 不使用","逗號(hào)表達(dá)式,但接受在聲明和函數(shù)參數(shù)中使用。 6.位操作 由于位操作在有符號(hào)問(wèn)題上操作復(fù)雜,推薦使用以下函數(shù)形式的宏,完成位操作。u/s分別表示unsigned和signed ushfleft,ushfright,uset,uclear,uisset,umask sshfleft,sshfright,sset,sclear,sisset,smask 7.語(yǔ)句 不使用do ... while/switch...case,推薦使用if else/while。 對(duì)于數(shù)組循環(huán)處理,推薦使用for (int i = 0; i < n; ++i) 這個(gè)標(biāo)準(zhǔn)結(jié)構(gòu),必要的索引通過(guò)第二個(gè)變量計(jì)算出來(lái)。 8.數(shù)組 使用數(shù)組+長(zhǎng)度兩個(gè)變量來(lái)表示一個(gè)數(shù)組。 除首地址外,建議其它元素地址使用&a[i]結(jié)構(gòu)來(lái)取得。 數(shù)組循環(huán)處理使用標(biāo)準(zhǔn)結(jié)構(gòu),例如,奇數(shù)索引的元素賦值為1: for (int i = 0; i < n; ++i) { a[2*i + 1] = 1; } 9.抽象數(shù)據(jù) 進(jìn)程有唯一性的抽象(全局狀態(tài)),定義為模塊。 通過(guò)void foo_init(void)或void foo_init(int argc, char** args)這樣的函數(shù)初始化。 通過(guò)void foo_close()這樣的函數(shù)關(guān)閉,通常這些調(diào)用會(huì)在main函數(shù)中完成。 一般在頭文件中聲明為: struct foo; struct foo* foo_new(); void foo_dosomething(struct foo* o, void* error); 在C文中定義訪問(wèn)struct foo和訪問(wèn)內(nèi)部成員的函數(shù) #include "namespace/foo.h" struct foo { 成員 }; 10.動(dòng)態(tài)抽象數(shù)據(jù) 頭文件中增加聲明 struct foo_ops { void (*op1)(void); void (*op2)(void); }; struct foo* foo_new(struct foo_ops* ops, void* data); 對(duì)應(yīng)的C文中,在struct foo中要增加定義 struct foo { struct foo_ops *ops; void* data; 其它成員 }; 11.錯(cuò)誤處理機(jī)制 主機(jī)制可以通過(guò)setjmp/longjmp這樣的全局狀態(tài)完成,這應(yīng)由main函數(shù)完成。 程序庫(kù)內(nèi)部應(yīng)提供非侵入式的錯(cuò)誤處理機(jī)制,例如返回錯(cuò)誤狀態(tài),慣例如下: int parse_options(int argc, char** args, struct error* err); 錯(cuò)誤狀態(tài)保存在err中,如果不關(guān)心錯(cuò)誤,可以通過(guò)傳入0來(lái)忽略,但函數(shù)本身應(yīng)把狀態(tài)記錄在全局的錯(cuò)誤對(duì)象上。 在前一個(gè)錯(cuò)誤未清除的情況,函數(shù)應(yīng)沒(méi)有動(dòng)作,也不修改全局錯(cuò)誤對(duì)象。輸入了錯(cuò)誤指針時(shí),把全局對(duì)象復(fù)制給錯(cuò)誤指針。 12.其它雜項(xiàng)和風(fēng)格問(wèn)題 不應(yīng)忽略{},總是使用{}來(lái)表明范圍。 使用//注釋,C99支持 使用按需聲明變量,C99支持 |
|