1. 程式人生 > >嵌入式C語言程式設計規範--變數、常量、巨集(個人規約)

嵌入式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等改變程式流程的語句;

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