0規範制定說明
技術人員設計程式的首要目的是用于技術人員溝通和交流,其次才是用于機器執行。程式的生命力在于使用者使用,程式的成長在于後期的維護及根據使用者需求更新和更新功能。如果你的程式隻能由你來維護,當你離開這個程式時,你的程式也和你一起離開了,這将給公司和後來接手的技術人員帶來巨大的痛苦和損失。是以,為了程式可讀、易了解、好維護,你的程式需要遵守一定的規範,你的程式需要設計。
“程式必須為閱讀它的人而編寫,隻是順便用于機器執行。”
——Harold Abelson 和 Gerald Jay Sussman
“編寫程式應該以人為本,計算機第二。”
——Steve McConnell
為提高産品代碼品質,指導儀表嵌入式軟體開發人員編寫出簡潔、可維護、可靠、可測試、高效、可移植的代碼,編寫了本規範。
本規範将分為完整版和精簡版,完整版将包括更多的樣例、規範的解釋以及參考材料(what & why),而精簡版将隻包含規則部分(what)以便查閱。
在本規範的最後,列出了一些業界比較優秀的程式設計規範,作為延伸閱讀參考材料。
本規範主要包含以下兩個方面的内容:
一:為形成統一程式設計規範,從編碼形式角度出發,本規範對标示符命名、格式與排版、注釋等方面進行了詳細闡述。
二:為編寫出高品質嵌入式軟體,從嵌入式軟體安全及可靠性出發,本規範對由于C語言标準、C語言本身、C編譯器及個人了解導緻的潛在危險進行說明及規避。
本規範适用于濟南金鐘電子衡器股份有限公司儀表台秤産品部嵌入式軟體的開發,也對其他嵌入式軟體開發起一定的指導作用。
原則:程式設計時必須堅持的指導思想。
規則:程式設計時需要遵循的約定,分為強制和建議(強制是必須遵守的,建議是一般情況下需要遵守,但沒有強制性)。
說明:對原則/規則進行必要的解釋。
執行個體:對此原則/規則從正、反兩個方面給出例子。
材料:擴充、延伸的閱讀材料。
Unspecified:未詳細說明的行為,這些是必須成功編譯的語言結構,但關于結構的行為,編譯器的編寫者有某些自由。例如C語言中的“運算次序”問題。這樣的問題有 22 個。 在某種方式上完全相信編譯器的行為是不明智的。編譯器的行為甚至不會在所有可能的結構中都是一緻的。
Undefined:未定義行為,這些是本質的程式設計錯誤,但編譯器的編寫者不一定為此給出錯誤資訊。相應的例子是無效參數傳遞給函數,或函數的參數與定義時的參數不比對。從安全性角度這是特别重要的問題,因為它們代表了那些不一定能被編譯器捕捉到的錯誤。
Implementation-defined:實作定義的行為,這有些類似于“unspecified ”問題,其主要差別在于編譯器要提供一緻的行為并記錄成文檔。換句話說,不同的編譯器之間功能可能會有不同,使得代碼不具有可移植性,但在任一編譯器内,行為應當是良好定義的。比如用在一個正整數和一個負整數上的整除運算“/ ”和求模運算符“% ”。存在76個這樣的問題。從安全性角度,假如編譯器完全地記錄了它的方法并堅持它的實作,那麼它可能不是那樣至關重要。盡可能的情況下要避免這些問題。
聲明(declaration):指定了一個變量的辨別符,用來描述變量的類型,是類型還是對象,
者函數等。聲明,用于編譯器(compiler)識别變量名所引用的實體。以下這些就是聲明:
extern int bar;
extern int g(int,int);
double f(int,double);[對于函數聲明,extern關鍵字是可以省略的。]
定義(definition):是對聲明的實作或者執行個體化。連接配接器(linker)需要它(定義)來引用記憶體實體。與上面的聲明相應的定義如下:
int bar;
int g(int lhs,int rhs) {returnlhs*rhs;}
double f(int i,double d) {returni+d;}
規則/原則<序号>(規則類型):規則内容。
[原始參考]
<序号>:每條規則都有一個序号,序号是按照章節目錄-**的形式,從數字1開始。例如,若在此章節有個規則的話,序号為0.5-1。
(規則類型):或者是‘強制’,或者是‘建議’。
規則内容:此條規則的具體内容。
[原始參考]:訓示了産生本條款或本組條款的可應用的主要來源。
1标示符命名規則
規則1.1-1(強制):辨別符(内部的和外部的)的有效字元不能多于31。
[UndefinedImplementation-defined]
說明:ISO 标準要求在内部辨別符之間前31 個字元必須是不同的,外部辨別符之間前6 個字元必須是不同的(忽略大小寫)以保證可移植性。我們這裡放寬了此要求,要求内部、外部标示符的有效字元不能多于31即可。這樣主要是便于編譯器識别,代碼清晰易讀,并保證可移植性。
規則1.1-2(強制):具有内部作用域的辨別符不應使用與具有外部作用域的辨別符相同的
名稱,在内部作用域裡具有内部标示符會隐藏外部辨別符。
說明:外部作用域和内部作用域的定義如下。檔案範圍内的辨別符可以看做是具有最外部
(outermost )的作用域;塊範圍内的辨別符看做是具有更内部(more inner)的作用域,連續嵌套的塊,其作用域更深入。如果内部作用域标示符和外部作用域标示符同名,内部作用域标示符會覆寫外部作用域标示符,導緻程式混亂。
執行個體:
INT8U test;
{
INT8U test; /*定義了兩個test */
test = 3; /*這将産生混淆 */
}
規則1.1-3(建議):具有靜态存儲期的對象或函數辨別符不能重用。
說明:不管作用域如何,具有靜态存儲期的辨別符都不應在系統内的所有源檔案中重用。它包含帶有外部連結的對象或函數,及帶有靜态存儲類辨別符的任何對象或函數。在一個檔案中存在一個具有内部連結的辨別符,而在另外一個檔案中存在着具有外部連結的相同名字的辨別符,或者存在兩個标示符相同的外部标示符。對使用者來說,這有可能導緻混淆。
test1.c
/**定義了一個靜态檔案域變量test1*/
static INT8U test1;
void test_fun(void)
{
INT8U test1; /*定義了一個同名的局部變量test1*/
test2.c
/**在另一個檔案又定義了一個具有外部連結的檔案域變量test1*/
INT8U test1;
原則1.1-4(強制):辨別符的命名要清晰、明了,有明确含義,同時使用完整的單詞或大家基本可以了解的縮寫,避免使人産生誤解。
說明:标示符的命名盡量做到見名知意,盡量讓别人快速了解你的代碼。
好的命名方法:
INT8U debug_message;
INT16U err_num;
不好的命名方法:
INT8U dbmesg;
INT16U en;
原則1.1-5(強制):常見通用的單詞縮寫盡量統一,不得使用漢語拼音、英語混用。
說明:簡短的單詞可以使用略去‘元音’字母形成縮寫,較長的單詞可以使用音節首字母
者單詞前幾個字母形成縮寫,針對大家公認的單詞縮寫要統一。對于特定的項目要使
用的專有縮寫應該注明或者做統一說明。
執行個體:常見單詞縮寫表(建議):
單詞
縮寫
argument
arg
buffer
buf
clock
clk
command
cmd
compare
cmp
configuration
cfg
device
dev
error
err
hexadecimal
hex
increment
inc
initialize
init
maximum
max
message
msg
minimum
min
parameter
param
previous
prev
register
reg
semaphore
sem
statistic
stat
synchronize
syn
temp
tmp
原則1.1-6(建議):用正确的反義詞組命名具有互斥意義的變量或相反動作的函數等。
執行個體:常見反義詞表:
正義
反義
add
remove
begin
end
create
destroy
insert
delete
first
last
get
release
decrement
put
lock
unlock
open
close
old
new
start
stop
next
source
target
show
hide
send
receive
destination
copy
pase
up
down
原則1.1-7(建議):标示符盡量避免使用數字編号,除非邏輯上需要。
#define DEBUG_0_MSG
#define DEBUG_1_MSG
應改為更有意義的定義:
#define DEBUG_WARN_MSG
#define DEBUG_ERR_MSG
材料:《代碼大全第2版》(Steve McConnell 著 金戈/湯淩/陳碩/張菲 譯 電子工業出版社
2006年3月)"第11章變量命的力量"。
規則1.2-1(強制):檔案名使用小寫字母。
說明:由于不同系統對檔案名大小寫處理不同,Windows不區分檔案名大小寫,而Linux區分。是以檔案名命名均采用小寫字母,多個單詞之間可使用”_”分隔符。
執行個體:disp.h os_sem.c
規則1.2-2(建議):工程源碼使用GB2312編碼方式。
說明:程式裡的注釋可能會使用中文,GB2312是簡體中文編碼,大部分的編輯工具和內建IDE環境都支援GB2312編碼,為避免中文亂碼,建議使用GB2312對源碼進行編碼。若需要轉換成其他編碼格式,可使用文本編碼轉換工具進行轉換。
規則1.2-3(強制):工程源碼使用版本管理工具進行版本管理。
說明:程式一般需要大量更新、修正、維護工作,且有時需要多人合作。使用版本管理工具可以幫助你提高工作效率。建議使用“Git”版本管理工具。
原則1.3-1(強制):變量命名應明确所代表的含義或者狀态。
說明:變量名稱可以使用名詞表述清楚的盡量使用名詞,使用名詞無法描述清楚時,使用形
容詞或者描述性的單詞+名詞的形式。變量一般為實體的屬性、狀态等資訊,使用上
述方案一般可以解決變量名的命名問題,如果出現命名很困難或者無法給出合理的命
名方式時,問題可能出現在整體設計上,請重新審視設計。
規則1.3-2(強制):全局變量添加”G_”字首,全局靜态變量添加” S_ ”,局部靜态變量添加”s_”字首。使用大小寫混合方式命名,大寫字母用于分割不同單詞。
說明:添加字首的原因有兩個。首先,使全局變量變得更醒目,提醒技術開發人員使用這些變量時要小心。其次,添加字首使全局變量和靜态變量變得和其他變量不一緻,提醒技術開發人員盡量少用全局變量。
/**出錯資訊 */
INT8U G_ErrMsg;
/**每秒鐘轉動圈數 */
static INT32U S_CirclePerSec;
規則1.3-3(強制):局部變量使用小寫字母,若标示符比較複雜,使用’_’分隔符。
說明:局部變量全部使用小寫字母,和全局變量有明顯區分,使讀者看到标示符就知道是何
種作用域的變量。
INT32U download_program_address;
規則1.3-4(強制):定義指針變量*緊挨變量名,全局指針變量使用大寫P字首”P_”,局部指針變量使用小寫p字首”p _”。
執行個體: INT8U *P_MsgAddress; /*全局變量*/
INT8U *p_msg; /*局部變量*/
原則1.4-1(強制):函數命名應該明确針對什麼對象做出了什麼操作。
說明:函數的功能是擷取、修改實體的屬性、狀态等,采用“動詞+名詞”的方式可以滿足上述需求,若出現使用此方式命名函數很困難或不能命名的情況,問題可能出現在整體設計上,請重新審視設計方案。
規則1.4-2(強制):具有外部連結的函數命名使用大小寫混合的方式,首字母大寫,用于分割不同單詞。
說明:函數具有外部連結屬性的含義是函數通過頭檔案對外聲明後,對其他檔案或子產品來
說是可見的。如果一個函數要在其他子產品或者檔案中使用,需要在頭檔案中聲明該函
數。另外,在頭檔案聲明函數,還可以促使編譯器檢查函數聲明和調用的一緻性。
char *GetErrMsg(ErrMsg *msg);
規則1.4-3(強制):具有檔案内部連結屬性的函數命名使用小寫字母,使用’_’分隔符分割不同單詞,且使用static關鍵字限制函數作用域。
說明:函數具有内部連結屬性的含義是函數隻能在子產品或檔案内部調用,對檔案或子產品外來
說是不可見的。如果一個函數僅在子產品内部或者檔案内部使用,需要限制函數連結圍,
使用static修飾符修飾函數,使其隻具有内部連結屬性。在源檔案中聲明一遍具有内
部連結的函數同樣具有促使編譯器檢查函數聲明和調用的一緻性。
static char get_key(void);
規則1.4-4(強制):函數參數使用小寫字母,各單詞之間使用“_”分割,盡量保持參數順序從左到右為:輸入、修改、輸出。
說明:函數參數順序為需輸入參數值(這個值一般不修改,若不需要修改使用const關鍵字
修飾),需修改的參數(這個參數輸入後用于提供資料,函數内部可以修改此參數),
輸出參數(這個參數是函數輸出值)。
規則1.5-1(強制):常量(#define定義的常量、枚舉、const定義的常量)的定義使用全大寫字母,單詞之間加 ’_’分割的命名方式。
#define PI_ROUNDED 3.14
const double PI_ROUNDED = 3.14;
enum weekday{ SUN,MON,TUE,WED,THU,FRI,SAT };
規則1.5-2(建議):常數宏定義時,十六進制數的表示方法為0xFF。
說明:前面0x中的x小寫,資料中的”A-F”大寫。
規則1.6-1(強制):新定義類型名的命名應該明确抽象對象的含義,新類型名使用大寫字母,單詞之間加’_’分割,新類型指針在類型名前增加字首”P_”。成員變量标示符前加類型名稱字首,首字母大寫用于區分各個單詞。
執行個體:typedef struct _STUDENT
StudentName;
StudentAge ;
......
}STUDENT , *P_ STUDENT;/* STUDENT 為新類型名稱,P_ STUDENT 為新類型指針名*/
2外觀布局
規則2.1.1-1(強制):頭檔案排版内容依次為包含的頭檔案、宏定義、類型定義、聲明變量、聲明函數。且各個種類的内容間空三行。
說明:頭檔案是子產品對外的公用接口。在頭檔案中定義的宏,可以被其他子產品引用。Project
中不建議使用全部變量,若使用則需在頭檔案裡對外聲明。子產品對外的函數接口在模
塊頭檔案裡聲明。
規則2.1.2-1(強制):源檔案排版内容依次為包含的頭檔案、宏定義、具有外部連結屬性的全局變量定義、子產品内部使用的static變量、具有内部連結的函數聲明、函數實作代碼。且各個種類的内容間空三行。
說明:子產品内部定義的宏,隻能在該子產品内部使用。隻在子產品内部使用的函數,需在源碼文
件中聲明,用于促使編譯器檢查函數聲明和調用的一緻性。
規則2.1.2-2(強制):程式塊采用縮進風格編寫,每級縮進4個空格。
說明:目前主流IDE都支援Tab縮進,使用Tab縮進需要打開和設定相關選項。宏定義、編譯開關、條件預處理語句可以頂格。
規則2.1.2-3(強制):if、for、do、while、case、switch、defaul、typedef等語句獨占一行,且這些關鍵字後需空一格。
說明:執行語句必須用縮進風格寫,屬于if、for、do、while、case、switch、default、typedef
等的下一個縮進級别。一般寫if、for、do、while等語句都會有成對出現的{}