MISRA 工業標準的C編程規範

admin @ 2014-03-25 , reply:0

    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>




[admin via 研發互助社區 ] MISRA 工業標準的C編程規範已經有1383次圍觀

http://cocdig.com/docs/show-post-42264.html