MISRA (The Motor Industry Software Reliability Association汽車工業軟件可靠性聯會)是位於英國的一個跨國汽車工業協會,其成員包括了大部分歐美汽車生產商。
其核心使命是為汽車工業提供服務和協助,幫助廠方開發安全的、高可靠性的嵌入式軟件。
這個組織最出名的成果是所謂的MISRA C Coding Standard,這一標準中包括了127條C語言編碼標準,通常認為,如果能夠完全遵守這些標準,則你的C代碼是易讀、可靠、可移植和易於維護的。
最近很多嵌入式開發者都以MISRA C來衡量自己的編碼風格,比如著名的uC/OS-II就得意地宣稱自己99%遵守MISRA標準。
而《嵌入式開發雜誌》也專門載文號召大家學習。編碼規範通常是一個公司自定的“土政策”,居然有人去做標準,而且還得到廣泛的認可,這不禁引起我強烈的興趣。
可惜這份標準的文本需要花錢去買,而且短短幾十頁,要價非常昂貴。 MISRA在網上公佈了一些文檔,其中有關於MISRA C Coding Standard的Clarification報告,從中間你可以大致猜到MISRA標準本身是什麼。
我仔細閱讀了這些文檔,並且通過閱讀其他一些介紹性文檔,大致了解了MISRA標準的主要內容。這些條款確有過人之處,對於C/C++語言工程項目的代碼質量管理能夠起到良好的指導性作用,對於大部分軟件開發企業來說,在MISRA的基礎上適當修改就可以形成自己的規範。
當然其中也有一些過於嚴苛的東西,這就需要各個開發部門靈活處理了。我個人的體會,編碼規範雖然很簡單,但是要完全執行,不折不扣,需要開發部門有很高的組織性和紀律性,並且有很好的代碼評審機制。因此,如果能夠嚴格地遵守編碼規範,本身就是一個開發部門實力的證明。
這裡不可能將所有規則一一列出(事實上正式文本我一條也沒看到),只列出一些比較有意思的條款,讓大家有機會了解MISRA的風格。
具體的內容,感興趣的朋友可以自己到www.misra.org.uk去了解。
Rule 1.嚴格遵循ANSI C89標準,不允許任何擴展。
Rule 3.如果要嵌入彙編語言,則必須將所有彙編語句包裝在C函數里,而且這些函數中只有彙編語句,沒有常規C語句。
Rule 7.不得使用三元操作符(? : )
Rule 10.不得殘留被註釋掉的廢代碼。
Rule 11.所有標識符不超過31字符。
Rule 12.不同名空間中的變數名不得相同。
例如:
typedef struct MyStruct {... } MyStruct; (違規)
struct Person {
char* name;
...
};
char name[32]; (違規)
Rule 13.不得使用char, int, float, double, long等基本類型,應該用自己定義的類型顯示表示類型的大小,如CHAR8, UCHAR8, INT16, INT32, FLOAT32, LONG64, ULONG64等。
Rule 14.不得使用類型char,必須顯示聲明為unsigned char或者signed char。
Rule 18.所有數字常數應當加上合適的後綴表示類型,例如51L, 42U, 34.12F等。
Rule 19.禁止使用八進制數。 (因為086U這樣的常數很容易引起誤解)。
Rule 21.不得定義與外部作用域中某個標識符同名的對象,以避免遮蓋外部作用域中的標識符。
Rule 23.具有文件作用域的對象盡量聲名為static的。
Rule 24.在同一個編譯單元中,同一個標識符不應該同事具有內部鏈接和外部鏈接的聲名。
這裡我略作說明:
我們通常將一些放在頭文件裡的變數聲名為“外部鏈接”的,如:
extern UINT32 g_count; //俗話叫變數聲明(對應於變數定義,不分配實際空間)
對於“使用”這個變數的.c文件來說,這很好,因為g_count始終保持外部鏈接性質。可是對於定義g_count(實際分配空間)的.c文件來說,如果包含了上述的頭文件,則在這個編譯單元里就發生了內部鏈接和外部鏈接的衝突。
解決辦法是,定義g_count的文件盡量不要包含聲名g_count的頭文件。個人感覺這不是任何時候都做得到的,尤其是在對付遺留代碼的時候。
Rule 25.具有外部鏈接性質的標識符應該只聲明一次。
Rule 27.外部對像不得在多個文件中聲名。
Rule 28.禁止使用register關鍵字。
Rule 29.自動對象(棧對象)使用前必須賦初值。
Rule 33.操作符&&和||的右側表達式不得具有副作用(side-effect)。
也就是說,象if (x == 20 && ++y == 19)這樣的表達式被禁止。
Rule 35.在返回布林值的表達式中不得出現賦值操作。
也就是說,我們常用的if (!(fp = fopen("fname", "r"))) { /* error */ }
被禁止。
Rule 37.不得對有符號數施加位操作,例如1 << 4將被禁止,必須寫1UL << 4;
Rule 39.不得對有符號表達式施加一元"-"操作符。
Rule 40.不得對有副作用的表達式施加sizeof操作符。
Rule 42.除了循環控制語句,不得使用逗號表達式。
Rule 44.禁止冗餘的顯式轉型。比如: double pi = (double) 3.1416F;
Rule 45.禁止從任意類型到指標的強制轉型,禁止從指標到任意類型的強制轉型。
例如:void* p = (void*)0xFFFF8888UL;
Rule 49.顯示測試值是否為零。
Rule 50.不得顯式判斷浮點數的相等性和不等性。
Rule 52.不得遺留“永遠不會用到”的代碼。
Rule 53.所有非空語句必須具有副作用。
Rule 55.除了switch語句,不得使用標號(label)。
Rule 56.不得使用goto.
Rule 57.不得使用continue。
Rule 58.除了switch語句,不得使用break.
Rule 59. if, else if, else, while, do..while, for語句塊必須使用{}括起。
Rule 60.任何if..else if語句,最後必須有一個收尾的else。例如:
if (ans == 'Y') {
...
}
else if (ans == 'N') {
...
}
else if (ans == 'C') {
...
}
else {
;
}
Rule 67.循環計數器的值不得在循環體內修改。
Rule 70.禁止任何直接和間接的遞歸函數呼叫。
Rule 82.每個函數只能有一個推出點。
Rule 86.如果一個函數可能返回錯誤信息,則呼叫後必須加以測試。
Rule 92.不應該使用#undef
Rule 95.不得將巨集作為參數傳給巨集函數
Rule 98.在一個巨集定義中,#或##符號只能出現一次。
Rule 101.禁止指標運算(代之以陣列下標運算)。
Rule 102.禁止超過兩級的指標。
Rule 104.禁止使用指向函數的非常量指標。
Rule 106.不得將棧對象的地址傳給外部作用域的對象。
************************************************** ******************
後面的規則針對實時嵌入式系統,對其他類型的開發未必適用,如:
Rule 118.禁止使用動態記憶體分配(也就是不得使用malloc, calloc和realloc)。
Rule 119.禁止使用errno。
Rule 120.禁止使用offsetof.
Rule 121.禁止使用<locale.h>
Rule 122.禁止使用setjmp, longjmp.
Rule 123.禁止使用<signal.h>
Rule 124.禁止使用<stdio.h>(不能用printf, scanf了!)
Rule 125.禁止使用atoi, atof, atol。 (這個我很贊成,建議使用strtol, strtod等函數)
Rule 126.禁止使用abort, exit, getenv。
Rule 127.禁止使用<time.h>
資料來源:https://b8807053.pixnet.net/blog/post/3612211
沒有留言:
張貼留言