嵌入式C語言編程規範--變量、常量、宏(個人規約)

一. 變量

1. 一個變量只有一個功能,不能把一個變量用作多種用途

說明:一個變量只用來表示一個特定功能,不能把一個變量作多種用途,即同一變量取值不同時,其代表的意義也不同。
示例:具有兩種功能的反例
    uint8_t getData(void)
    {
        uint8_t data;
        data = 3;
        data = get(data); /* data具有兩種功能:位置和函數get的返回值 */
        return data;
    }
正確做法:使用兩個變量
    uint8_t getData(void)
    {
        uint8_t num;
         uint8_t data;
        num = 3;
        data = get(data);
        return data;

    }

2. 結構功能單一;不要設計面面俱到的數據結構;

說明:有關聯的一組信息纔是構成一個結構體的基礎,結構的定義應該可以明確的描述一個對象,而不是一組相關性不強的數據的集合。

設計結構時應力爭使結構代表一種現實事務的抽象,而不是同時代表多種。結構中的各元素應代表同一事務的不同側面,而不應把描述沒有關係或關係很弱的不同事務的元素放到同一結構中。

3. 不用或者少用全局變量;

說明:單個文件內部可以使用static的全局變量,可以將其理解爲類的私有成員變量。

全局變量應該是模塊的私有數據,不能作用對外的接口使用,使用static類型定義,可以有效防止外部文件的非正常訪問。

4. 防止局部變量與全局變量同名;

說明:儘管局部變量和全局變量的作用域不同而不會發生語法錯誤,但容易使人誤解。

5. 嚴禁使用未經初始化的變量作爲右值;

說明:可以避免未初始化錯誤。

6.  構造僅有一個模塊或函數可以修改、創建,而其餘有關模塊或函數只訪問的全局變量,防止多個不同模塊或函數都可以修改、創建同一全局變量的現象;

7. 在首次使用前初始化變量,初始化的地方離使用的地方越近越好;

說明:未初始化變量是C和C++程序中錯誤的常見來源。在變量首次使用前確保正確初始化。在較好的方案中,變量的定義和初始化要做到親密無間。

8. 明確全局變量的初始化順序,避免跨模塊的初始化依賴;

說明:系統啓動階段,使用全局變量前,要考慮到該全局變量在什麼時候初始化,使用全局變量和初始化全局變量,兩者之間的時序關係,誰先誰後,一定要分析清楚。

9. 儘量減少沒有必要的數據類型默認轉換與強制轉換;

說明:當進行數據類型強制轉換時,其數據的意義、轉換後的取值等都有可能發生變化,而這些細節若考慮不周,就很有可能留下隱患。

二.  宏、常量

1. 用宏定義表達式時,要使用完備的括號;

說明:因爲宏只是簡單的代碼替換,不會像函數一樣先將參數計算後,再傳遞。

示例:如下定義的宏都存在一定的風險

#define RECTANGLE_AREA(a, b) a * b

#define RECTANGLE_AREA(a, b) (a * b)

#define RECTANGLE_AREA(a, b) (a) * (b)

正確的定義應爲:

#define RECTANGLE_AREA(a, b) ((a) * (b))

這是因爲:

如果定義#define RECTANGLE_AREA(a, b) a * b 或#define RECTANGLE_AREA(a, b) (a * b)

則c/RECTANGLE_AREA(a, b) 將擴展成c/a * b , c 與b 本應該是除法運算,結果變成了乘法運算,造成錯誤。

如果定義#define RECTANGLE_AREA(a, b) (a) * (b)

則RECTANGLE_AREA(c + d, e + f)將擴展成:(c + d * e + f), d與e 先運算,造成錯誤。

2. 將宏所定義的多條表達式放在大括號中

說明:更好的方法是多條語句寫成do while(0)的方式。
示例:看下面的語句,只有宏的第一條表達式被執行。
    #define FOO(x) \
        printf("arg is %d\n", x); \
        do_something_useful(x);
爲了說明問題,下面for語句的書寫稍不符規範
    for (blah = 1; blah < 10; blah++)
        FOO(blah)
用大括號定義的方式可以解決上面的問題:
    #define FOO(x) { \
        printf("arg is %s\n", x); \
        do_something_useful(x); \
    }
但是如果有人這樣調用:
    if (condition == 1)
        FOO(10);
    else
        FOO(20);
那麼這個宏還是不能正常使用,所以必須這樣定義才能避免各種問題:
    #define FOO(x) do { \
        printf("arg is %s\n", x); \
        do_something_useful(x); \
       } while(0)

用do-while(0)方式定義宏,完全不用擔心使用者如何使用宏,也不用給使用者加什麼約束。

3. 使用宏時,不允許參數發生變化;

4. 不允許直接使用魔鬼數字;

5. 除非必要,應儘可能使用函數代替宏;

說明:宏對比函數,有一些明顯的缺點:①. 宏缺乏類型檢查,不如函數調用檢查嚴格;②. 以宏形式寫的代碼難以調試難以打斷點,不利於定位問題;③. 宏如果調用的很多,會造成代碼空間的浪費,不如函數空間效率高;

6. 常量建議使用const定義代替宏;

說明:當報錯時,只會顯示常量,不會顯示宏定義的名字,查找時,很費勁。

7. 宏定義中儘量不使用return、goto、continue、break等改變程序流程的語句;

說明:如果在宏定義中使用這些改變流程的語句,很容易引起資源泄漏問題,使用者很難自己察覺。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章