1. 程式人生 > >嵌入式C編碼規範

嵌入式C編碼規範

0規範制定說明

0.1箴言

技術人員設計程式的首要目的是用於技術人員溝通和交流,其次才是用於機器執行。程式的生命力在於使用者使用,程式的成長在於後期的維護及根據使用者需求更新和升級功能。如果你的程式只能由你來維護,當你離開這個程式時,你的程式也和你一起離開了,這將給公司和後來接手的技術人員帶來巨大的痛苦和損失。因此,為了程式可讀、易理解、好維護,你的程式需要遵守一定的規範,你的程式需要設計。

“程式必須為閱讀它的人而編寫,只是順便用於機器執行。”

              ——Harold Abelson 和 Gerald Jay Sussman

“編寫程式應該以人為本,計算機第二。”

——Steve McConnell

0.2簡介:

為提高產品程式碼質量,指導儀表嵌入式軟體開發人員編寫出簡潔、可維護、可靠、可測試、高效、可移植的程式碼,編寫了本規範。 

本規範將分為完整版和精簡版,完整版將包括更多的樣例、規範的解釋以及參考材料(what & why),而精簡版將只包含規則部分(what)以便查閱。

在本規範的最後,列出了一些業界比較優秀的程式設計規範,作為延伸閱讀參考材料。

本規範主要包含以下兩個方面的內容:

一:為形成統一程式設計規範,從編碼形式角度出發,本規範對標示符命名、格式與排版、註釋等方面進行了詳細闡述。

二:為編寫出高質量嵌入式軟體,從嵌入式軟體安全及可靠性出發,本規範對由於C語言標準、C語言本身、C編譯器及個人理解導致的潛在危險進行說明及規避。

0.3適用範圍:

本規範適用於濟南金鐘電子衡器股份有限公司儀表檯秤產品部嵌入式軟體的開發,也對其他嵌入式軟體開發起一定的指導作用。

0.4術語定義

0.4.1 規範術語

原則:程式設計時必須堅持的指導思想。

規則:程式設計時需要遵循的約定,分為強制和建議(強制是必須遵守的,建議是一般情況下需要遵守,但沒有強制性)。

說明:對原則/規則進行必要的解釋。 

例項:對此原則/規則從正、反兩個方面給出例子。

材料:

擴充套件、延伸的閱讀材料。

 

Unspecified:未詳細說明的行為,這些是必須成功編譯的語言結構,但關於結構的行為,編譯器的編寫者有某些自由。例如C語言中的“運算次序”問題。這樣的問題有 22 個。 在某種方式上完全相信編譯器的行為是不明智的。編譯器的行為甚至不會在所有可能的結構中都是一致的。

Undefined:未定義行為,這些是本質的程式設計錯誤,但編譯器的編寫者不一定為此給出錯誤資訊。相應的例子是無效引數傳遞給函式,或函式的引數與定義時的引數不匹配。從安全性角度這是特別重要的問題,因為它們代表了那些不一定能被編譯器捕捉到的錯誤。

Implementation-defined:實現定義的行為,這有些類似於“unspecified ”問題,其主要區別在於編譯器要提供一致的行為並記錄成文件。換句話說,不同的編譯器之間功能可能會有不同,使得程式碼不具有可移植性,但在任一編譯器內,行為應當是良好定義的。比如用在一個正整數和一個負整數上的整除運算“/ ”和求模運算子“% ”。存在76個這樣的問題。從安全性角度,假如編譯器完全地記錄了它的方法並堅持它的實現,那麼它可能不是那樣至關重要。儘可能的情況下要避免這些問題。

0.4.2  C語言相關術語

宣告(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;} 

0.5規則的形式

規則/原則<序號>(規則型別):規則內容。                                 

[原始參考]

<序號>:每條規則都有一個序號,序號是按照章節目錄-**的形式,從數字1開始。例如,若在此章節有個規則的話,序號為0.5-1。

(規則型別):或者是‘強制’,或者是‘建議’。

規則內容:此條規則的具體內容。

[原始參考]:指示了產生本條款或本組條款的可應用的主要來源。

1標示符命名規則

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

increment

decrement

put

get

add

delete

lock

unlock

open

close

min

max

old

new

start

stop

next

previous

source

target

show

hide

send

receive

source

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.2-1(強制):檔名使用小寫字母。

說明:由於不同系統對檔名大小寫處理不同,Windows不區分檔名大小寫,而Linux區分。所以檔名命名均採用小寫字母,多個單詞之間可使用”_”分隔符。

例項:disp.h   os_sem.c

 

規則1.2-2(建議):工程原始碼使用GB2312編碼方式。

說明:程式裡的註釋可能會使用中文,GB2312是簡體中文編碼,大部分的編輯工具和整合IDE環境都支援GB2312編碼,為避免中文亂碼,建議使用GB2312對原始碼進行編碼。若需要轉換成其他編碼格式,可使用文字編碼轉換工具進行轉換。

 

規則1.2-3(強制):工程原始碼使用版本管理工具進行版本管理。

說明:程式一般需要大量更新、修正、維護工作,且有時需要多人合作。使用版本管理工具可以幫助你提高工作效率。建議使用“Git”版本管理工具。

 

1.3變數命名規則

原則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.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.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.6-1(強制):新定義型別名的命名應該明確抽象物件的含義,新型別名使用大寫字母,單詞之間加’_’分割,新型別指標在型別名前增加字首”P_”。成員變數標示符前加型別名稱字首,首字母大寫用於區分各個單詞。

例項:typedef struct  _STUDENT

{

    StudentName;

      StudentAge  ;

    ......

}STUDENT , *P_ STUDENT;/* STUDENT 為新型別名稱,P_ STUDENT 為新型別指標名*/

2外觀佈局

2.1排版與格式

2.1.1 標頭檔案排版

規則2.1.1-1(強制):標頭檔案排版內容依次為包含的標頭檔案、巨集定義、型別定義、宣告變數、宣告函式。且各個種類的內容間空三行。

說明:標頭檔案是模組對外的公用介面。在標頭檔案中定義的巨集,可以被其他模組引用。Project

      中不建議使用全部變數,若使用則需在標頭檔案裡對外宣告。模組對外的函式介面在模

      塊標頭檔案裡宣告。

2.1.2 原始檔排版

規則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等語句獨佔一行,且這些關鍵字後需空一格。

說明:執行語句必須用縮排風格寫,屬於iffordowhilecaseswitchdefaulttypedef

等的下一個縮排級別。一般寫iffordowhile等語句都會有成對出現的{}‟,if

fordowhile等語句後的執行語句建議增加成對的“{}”; 如果if/else語句塊中只

有一條語句,也需增加“{}”。

例項:

 

for  (i  =  0; i  <  max_num; i++)

{

for  (j  =  0;  j  <  max_num; j++)

{

        If (name_found)

                  {

                                                        語句

}

else

{

              語句

}

}

}

 

規則2.1.2-4(強制):進行雙目運算、賦值時,操作符之前、之後要加空格;進行非對等操作時,如果是關係密切的立即操作符(如->),後不應加空格。

說明:採用這種方式書寫程式碼,主要目的是使程式碼更清晰,使關鍵操作符更突出

例項:

(1)    比較操作符, 賦值操作符"="、 "+=",算術操作符"+"、"%",邏輯操作符"&&"、"&",

位域操作符"<<"、"^"等雙目操作符的前後加空格。

If (a > b)

a += 2;

b = a ^ 3;

(2)    "!"、"~"、"++"、"--"、"&"(地址操作符)等單目操作符前後不加空格。

Search_dowm = !true;

a++;

(3)    "->"、"."、”[]”前後不加空格。

Weight         =  G_Car->weight;

eye           =  People.eye;

array[8]        =  8;

 

規則2.1.2-5(建議):一行只定義一個變數,一行只書寫一條執行語句,多行同類操作時操作符儘量保持對齊。

說明:一行定義一個變數,一行只書寫一條執行語句,方便註釋,多行同類操作對齊美觀、整潔。

例項:

events_rdy        =  OS_FALSE;

events_rdy_nbr =  0;

events_stat    =  OS_STAT_RDY;

pevents           =  pevents_pend;

pevent            = *pevents;

 

規則2.1.2-6(建議):函式內部區域性變數定義和函式語句之間應空三行。

說明:區域性變數定義和函式語句是相對獨立的,而且空三行可以更清晰地表示出這種獨立性。

 

3註釋

3.1註釋原則

原則3.1-1(強制):註釋的內容要清楚、明瞭,含義準確,在程式碼的功能、意圖層次上進行註釋。

說明:註釋的目的是讓讀者快速理解程式碼的意圖。註釋不是為了名詞解釋(what),而是說

明用途(why)。

例項:

          如下注釋純屬多餘:

                   ++i;                                     // i增加1                                   

if (data_ready)                          /* 如果data_ready為真 */

如下注釋無任何參考價值:

// 時間有限,現在是:04,根本來不及想為什麼,也沒人能幫我說清楚

 

原則3.1-2(強制):註釋應分為兩個角度進行,首先是應用角度,主要是告訴使用者如何使用介面(即你提供的函式),其次是實現角度,主要是告訴後期升級、維護的技術人員實現的原理和細節。

說明:每一個產品都可以分為三個層次,產品本身是一個層次,這個層次之下的是你使用

的更小的元件,這個層次之上的是你為別人提供的服務。你這個產品的存在的價值

就在於把最底層的小部件的使用細節隱藏,同時給最上層的使用者提供方便、簡潔的

使用介面,滿足用於需求。從這個角度來看軟體的註釋,你應該時刻想著你寫的註釋

是給那一層次的人員看的,如果是使用者,那麼你應該注重描述如何使用,如果是後期

維護者,那麼你應該注重原理和實現細節。

 

原則3.1-3(強制):修改程式碼時,應維護程式碼周邊的註釋,使其程式碼和註釋一致,不再使用的註釋應刪除。

說明:註釋的目的在於幫助讀者快速理解程式碼使用方法或者實現細節,若註釋和程式碼不一

      致,會起到相反的作用。建議在修改程式碼前應該先修改註釋。

 

規則3.1-4(建議):程式碼段不應被“註釋掉”(comment out )。

說明:當原始碼段不需要被編譯時,應該使用條件編譯來完成(如帶有註釋的#if或#ifdef 結

構)。為這種目的使用註釋的開始和結束標記是危險的,因為C 不支援/**/巢狀的注

釋,而且已經存在於程式碼段中的任何註釋將影響執行的結果。

3.2檔案註釋

規則3.2-1(強制):檔案註釋需放到檔案開頭,具體格式見例項。

例項:

stm32f10x_dac.h

  /**

******************************************************************************

  * @file     stm32f10x_dac.h

* @brief    Thisfile contains all the functions prototypes for the DAC firmware

  *          library.

  * @author  MCD Application Team

  * @version  V3.5.0

  * @date    11-March-2014

* @par     Modification:新增函式,支援********<br>

*                     History

*          Version:V3.0.1 <br>

*          Author:***<br>

*          Modification:新增函式,支援********<br>

*          Version:V3.0.0 <br>

*          Author:***<br>

*          Modification:新增函式,支援********<br>

  *************************************************************************

  * @attention

*********************************************************

*/

說明:註釋格式可被doxygen工具識別,其中@file、@brief、@author等是doxygen工具識別的關鍵字,註釋內容可以為中文。

3.3函式註釋

規則3.3-1(強制):函式註釋分為標頭檔案中函式原型宣告時的註釋和原始檔中函式實現時的註釋。標頭檔案中的註釋注重函式使用方法和注意事項,原始檔中的註釋注重函式實現原理和方法。具體格式見例項。

說明:函式原型宣告的註釋按照doxygen工具可以識別的格式進行註釋,用於doxygen工具

生成標頭檔案資訊以及函式間的呼叫關係資訊。原始碼實現主要是註釋函式實現原理及

修改記錄,不需按照doxygen工具要求的註釋格式進行註釋。

例項:

  標頭檔案函式原型宣告註釋:

/**

********************************************************************

* @brief     Configures the discontinuous mode for theselected ADC regular

*           group channel.

* @param    ADCx:where x can be 1, 2 or 3 to select the ADC peripheral.

* @param    Number:specifies the discontinuous mode regular channel

*           count value. This number must be between 1 and8.

* @retval    None

* @par      Usage:

*           ADC_DiscModeChannelCountConfig(ADC1,6);<br>

* @par      Tag:

*           此函式不能在中斷裡呼叫。

********************************************************************

*/

void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, INT8U_tNumber);

 

原始檔函式實現註釋:

/*

********************************************************************

  * @brief    Configures the discontinuousmode for the selected ADC regular

  *          group channel.

  * @param   ADCx: where x can be 1, 2 or 3 toselect the ADC peripheral.

  * @param   Number: specifies the discontinuousmode regular channel

  *          count value. This number must bebetween 1 and 8.

  * @retval   None

  * @par     Modification:修改了********<br>

*                       History

  *          Modified by:***<br>

*           Date: 2013-10-10

  *          Modification:修改了********<br>

********************************************************************

  */

void ADC_DiscModeChannelCountConfig(ADC_TypeDef*ADCx, INT8U_t Number)

{

    賦值語句*********;       /*關鍵語句的註釋           */

語句***********;         /*關鍵語句的註釋格式       */

語句*******;             /*實現*****************功能*/

}

 

3.4常量及全域性變數註釋

規則3.3-1(強制):常量、全域性變數需要註釋,註釋格式見例項。

例項:

/** Description of the macro */

#define XXXX_XXX_XX           0

 

/**Description of global variable */

INT8U  G_xxx  =  0;

說明:若全域性變數在.c檔案中定義,又在.h檔案中宣告,則在標頭檔案中使用doxygen

格式註釋,在原始碼檔案中使用  /* Description of the globalvariable */的形式。防止doxygen生成兩遍註釋文件資訊。

 

3.5區域性變數及語句註釋

規則3.3-1(強制):區域性變數,函式實現關鍵語句需要註釋,註釋格式見例項。

例項:

      *pq->OSQIn++ = pmsg;              /* Insert message into queue          */

       pq->OSQEntries++;                /* Update the nbr of entries in the queue*/

      if (pq->OSQIn== pq->OSQEnd)

          {          

pq->OSQIn = pq->OSQStart;    /* Wrap IN ptr if we are at end of queue */

      }

說明:區域性變數,關鍵語句需要註釋,從功能和意圖上進行註釋,而不是程式碼的重複。多條註釋語句儘量保持對齊,實現美觀,整潔。

材料:

1.        《程式碼整潔之道》(RobertC.Martin 著 韓磊 譯 人民郵電出版社2010年1月)第四章"註釋”。

2.        《Doxygen中文手冊》

4專案版本號命名規範

專案版本號管理是專案管理的重要方面,我們根據專案不同的開發階段制定了不同的版本號命名規範。專案開發過程一般分為前期開發測試階段、釋出階段、維護階段這三個主要階段,我們分別制定了命名規範。

4.1開發、測試階段版本號命名

規則4.1-1(強制):處於開發、除錯階段的專案,版本號使用“V0.yz”的形式。

說明:處於新開發、除錯階段的專案,版本號使用“V0.yz” 的形式,比如新開發的專案正處在開發、除錯階段,這時可以使用“ V0.10 ”這樣的版本號。你認為完成了新的功能模組或整體架構做了很大的修改,可以根據情況增加 Y 或者 Z的值。比如,你開發階段在“ V0.10 ”基礎上新增加了一個功能模組你可以將版本號改為“V0.11”,做了比較大的修改,你可以將版本號定為“V0.20”。

4.2正式釋出階段版本號命名

規則4.2-1(強制):處於正式釋出階段的專案,版本號使用“Vx.y”的形式。

說明:處於正式釋出的專案版本號使用“Vx.y”的形式。比如,你釋出了一個正式面向市場的專案,你可以使用“V1.0”作為正式的版本號。在“V1.0”基礎上增加功能的正式版本,你可以使用“V1.1”作為下一次正式版本的版本號,在“V1.0”基礎上修正了大的BUG或者做了很大的改動,你可以使用“V2.0”作為下一次正式版本號。

4.3維護階段版本號命名

規則4.3-1(強制):處於維護階段的專案,版本號使用“Vx.yz”的形式。

說明:處於維護階段的專案版本號使用“Vx.yz”的形式。比如在"V1.1"的基礎上修改了一個功能實現演算法以實現高效率,則可以使用"V1.11" 來表示這是在正式釋出版本“V1.1”的基礎上進行的一次修正,再次修正可以使用“V1.12”。

 

5嵌入式軟體安全性相關規範

5.1標頭檔案

原則5.1-1(強制):標頭檔案用於宣告模組對外介面,包括具有外部連結的函式原型宣告、全域性變數宣告、定義的型別宣告等。

 

說明:標頭檔案是模組(Module)或單元(Unit)的對外介面。標頭檔案中應放置對外部的聲

明,如對外提供的函式宣告、巨集定義、型別定義等。內部使用的函式宣告不應放在

標頭檔案中。 內部使用的巨集、列舉、結構定義不應放入標頭檔案中。變數定義不應放

在標頭檔案中,應放在.c檔案中。 變數的宣告儘量不要放在標頭檔案中,亦即儘量不要

使用全域性變數作為介面。變數是模組或單元的內部實現細節,不應通過在標頭檔案中

宣告的方式直接暴露給外部,應通過函式介面的方式進行對外暴露。即使必須使用

全域性變數,也只應當在.c中定義全域性變數,在.h中僅宣告變數為全域性的。

材料:《C語言介面與實現》(David R. Hanson著 傅蓉 周鵬 張昆琪權威 譯 機械工業出

版社 2004年1月)(英文版: "C Interfaces and Implementations")

規則5.1-2(強制):只能通過包含標頭檔案的方式使用其他.c提供的介面,禁止在.c中通過extern的方式使用外部函式介面、變數。

說明:若a.c使用了b.c定義的foo()函式 ,則應當在b.h中宣告externint foo(int input);並

在a.c中通過#include <b.h>來使用foo。禁止通過在a.c中寫externint foo(int input);

來使用foo,後面這種寫法容易在foo改變時可能導致宣告和定義不一致。

 

規則5.1-3(強制):使用#define定義保護符,防止標頭檔案重複包含。

說明:多次包含一個頭檔案可以通過認真的設計來避免。如果不能做到這一點,就需要採取

阻止標頭檔案內容被包含多於一次的機制。通常的手段是為每個檔案配置一個巨集,當頭

檔案第一次被包含時就定義這個巨集,並在標頭檔案被再次包含時使用它以排除檔案內

容。所有標頭檔案都應當使用#define 防止標頭檔案被多重包含,命名格式FILENAME_H_,

其中FILENAME 為標頭檔案的名稱。

例項:

      若檔名為:stm32f10x_adc.h。

     

#ifndef          STM32F10x_DAC_H_

#define             STM32F10x_DAC_H_

…………

受保護的程式碼

#endif

5.2預處理命令

規則5.2-1(強制):C的巨集只能擴充套件為用大括號括起來的初始化、常量、小括號括起來的

表示式、型別限定符、儲存類識別符號或do-while-zero 結構。

說明:這些是巨集當中所有可允許使用的形式。儲存類識別符號和型別限定符包括諸如extern 、

static和const這樣的關鍵字。使用任何其他形式的#define 都可能導致非預期的行為,

或者是非常難懂的程式碼。特別的,巨集不能用於定義語句或部分語句,除了do-while 結

構。巨集也不能重定義語言的語法。巨集的替換列表中的所有括號,不管哪種形式的 ()、

{} 、[]  都應該成對出現。 do-while-zero 結構(見下面例項)是在巨集語句體中唯一可接受的具有完整語句的形式。do-while-zero 結構用於封裝語句序列並確保其是正確的。

注意:在巨集語句體的末尾必須省略分號。

   例項

                   以下是合理的巨集定義:

       #define   PI 3.14159F                  /*Constant */

        #define  XSTAL 10000000                /*Constant */

        #define  CLOCK (XSTAL / 16)             /*Constant expression */

        #define   PLUS2(X)  ( (X) + 2 )            /* Macro expanding to expression */

        #define  STOR extern                  /*storage class specifier */

        #define   INIT(value)  { (value), 0, 0 }       /*braced initialiser */

        #define   READ_TIME_32() \

        do  { \

                     DISABLE_INTERRUPTS(); \

                    time_now = (INT32U) TIMER_HI << 16; \

                    time_now = time_now | (INT32U) TIMER_LO; \

                     ENABLE_INTERRUPTS(); \

           } while(0)                           /* example of do-while-zero */

             以下是不合理的巨集定義:

                   #define unsigned int  long     /* use typedef instead */

        #defineSTARTIF  if(           /* unbalanced () and languageredefinition */

 

規則5.2-2(強制):在定義函式巨集時,每個引數例項都應該以小括號括起來。

例項:

          一個abs 函式可以定義成:

     #define  abs (x) ( ( (x) >= 0 ) ? (x) : -(x) )

不能定義成:

    #define   abs(x)  ( ( (x) >= 0 ) ? x : -x )

如果不堅持本規則,那麼當前處理器替代巨集進入程式碼時,操作符優先順序將不會給出要

求的結果。

考慮前面第二個不正確的定義被替代時會發生什麼:

       z = abs ( a – b );

將給出如下結果:

       z = ( ( a – b >= 0 ) ? a – b : -a – b );

子表示式  – a - b  相當於 (-a)-b ,而不是希望的 –(a-b) 。把所有引數都括進小括號中就可以避免這樣的問題。

 

 

規則5.2-3(建議):使用巨集時,不允許引數數值發生變化。

例項:

         如下用法可能導致錯誤。

#define SQUARE(a) ((a) * (a))

int a = 5;

int b;

b = SQUARE(a++);           /*結果:a = 7,即執行了兩次增。

 

正確的用法是:

b = SQUARE(a);

a++;                                      /*結果:a = 6,即只執行了一次增*/

 

同樣建議在呼叫函式時,引數也不要變化,如果某次軟體升級將其中一個介面由函式實現轉換成巨集,那引數數值發生變化的呼叫將產生非預期效果。

 

規則5.2-4(建議):除非必要,應儘可能使用函式代替巨集。

 

說明:巨集能提供比函式優越的速度,但是沒有引數檢查機制,不當的使用可能產生非預期後果。

 

5.3型別及型別轉換

規則5.3-1(強制):應該使用標明瞭大小和符號的typedef代替基本資料型別。不應使用基本數值型別char、int、short、long、float和double,而應使用typedef進行型別的定義。

說明:為了程式的跨平臺移植性,我們使用typedef定義指明瞭大小和符號的資料型別。

例項:

       此例項是根據keil for ARM的資料型別大小進行的定義。

 

No.

基本資料型別

Typedef定義

1

typedef unsigned char

BOOLEAN

2

typedef unsigned char

INT8U

3

typedef signed   char

INT8S

4

typedef unsigned short

INT16U

5

typedef signed   short

INT16S

6

typedef unsigned int

INT32U

7

typedef signed   int

INT32S

8

typedef float

FP32

9

typedef double

FP64

 

應根據硬體平臺和編譯器的資訊對基本型別進行定義。

 

規則5.3-2(建議):浮點應用應該適應於已定義的浮點標準。

說明:浮點運算會帶來許多問題,一些問題(而不是全部)可以通過適應已定義的標準來克服。其中一個合適的標準是  ANSI/IEEE Std 754 [1] 。

 

5.3.1 顯式資料型別轉換

C 語言給程式設計師提供了相當大的自由度並允許不同數值型別可以自動轉換。由於某些功能

性的原因可以引入顯式的強制轉換,例如:

1.        用以改變型別使得後續的數值操作可以進行

2.        用以擷取數值

3.        出於清晰的角度,用以執行顯式的型別轉換

為了程式碼清晰的目的而插入的強制轉換通常是有用的,但如果過多使用就會導致程式的

可讀性下降。正如下面所描述的,一些隱式轉換是可以安全地忽略的,而另一些則不能。

規則5.3.1-1(強制):強制轉換隻能向表示範圍更窄的方向轉換,且與被轉換物件的類

型具有相同的符號。浮點型別值只能強制轉換到更窄的浮點型別。

說明:這條規則主要是要求需要強制轉換時,須明確被轉換物件的表示範圍及轉換後的表示

範圍。轉換時儘量保持符號一致,不同符號物件之間不應出現強制轉換。向更寬資料

範圍轉換並不能提高資料精確度,並沒有實際意義。在程式中儘量規劃好變數範圍,

儘量少使用強制轉換。

 

規則5.3.1-2(強制):如果位運算子 ~  和 <<  應用在基本型別為unsigned char或unsigned

short 的運算元,結果應該立即強制轉換為運算元的基本型別。

說明:當這些操作符(~ 和<<)用在 small integer 型別(unsigned char 或unsigned short )時,運算之前要先進行整數提升,結果可能包含並非預期的高階資料位。

例如:

         INT8U port= 0x5aU;

        INT8U result_8;

        INT16U result_16;

        INT16U mode;

       result_8 = (~port) >> 4;    /* 不合規範 */

~port的值在16位機器上是 0xffa5 ,而在 32 位機器上是 0xffffffa5 。在每種情況下,result的值是0xfa ,然而期望值可能是0x0a 。這樣的危險可以通過如下所示的強制轉換來避免:

result_8 = ( (INT8U) (~port  )) >> 4;            /* 符合規範 */

result_16 = ( (INT16U ) (~(INT16U) port ) ) >> 4 ;   /*符合規範  */

當<<操作符用在 smallinteger 型別時會遇到類似的問題,高階資料位被保留下來。

例如:

result_16 = ( ( port << 4 ) & mode ) >> 6 ;         /*不符合規範 */

result_16 的值將依賴於 int 實現的大小。附加的強制轉換可以避免任何模糊性。

result_16 = ( ( INT16U) ( ( INT16U ) port << 4 ) & mode )>> 6 ;  /*  符合規範 */

5.3.2隱式型別轉換

規則5.3.2-1(強制):以下型別之間不應該存在隱式型別轉換。

1)        有符號和無符號之間沒有隱式轉換

2)        整型和浮點型別之間沒有隱式轉換

3)        沒有從寬型別向窄型別的隱式轉換

4)        函式引數沒有隱式轉換

5)        函式的返回表示式沒有隱式轉換

6)        複雜表示式沒有隱式轉換

5.3.3 整數字尾

規則5.3.3-1(強制):字尾“U”應該用在所有unsigned 型別的常量上。

整型常量的型別是混淆的潛在來源,因為它依賴於許多因素的複雜組合,包括:

1)        常數的量級

2)        整數型別實現的大小

3)        任何字尾的存在

4)        數值表達的進位制(即十進位制、八進位制或十六進位制)

例如,整型常量“40000”在32位環境中是 int 型別,而在 16位環境中則是long 型別。

值0x8000 在16位環境中是 unsigned int 型別,而在 32 位環境中則是(signed )int 型別。

注意:

1)        任何帶有“U”字尾的值是unsigned 型別

2)        一個不帶字尾的小於231的十進位制值是signed 型別

但是:

1)        不帶字尾的大於或等於215的十六進位制數可能是 signed 或unsigned 型別

2)        不帶字尾的大於或等於231的十進位制數可能是 signed 或unsigned 型別

常量的符號應該明確。符號的一致性是構建良好形式的表示式的重要原則。如果一個常

數是unsigned 型別,為其加上“U”字尾將有助於避免混淆。當用在較大數值上時,字尾也許是多餘的(在某種意義上它不會影響常量的型別);然而後綴的存在對程式碼的清晰性是種有價值的幫助。

相關推薦

嵌入式C編碼規範

0規範制定說明 0.1箴言 技術人員設計程式的首要目的是用於技術人員溝通和交流,其次才是用於機器執行。程式的生命力在於使用者使用,程式的成長在於後期的維護及根據使用者需求更新和升級功能。如果你的程式只能由你來維護,當你離開這個程式時,你的程式也和你一起離開了,這將給公司和後來接手的技術人員帶

C++編碼規範

c++ 編碼規範 C++編碼規範 編碼規範,說代碼質量重要組成部分,如何防止循環引用?防止每次都重新編譯.代碼修改的最小編譯。不僅僅是一個簡單的變量書寫格式,更多是函數參數應如何去指定,指針的使用應該如何去包含? 為什麽禁止使用缺省函數等等...c++編碼

C# 編碼規範

接口 可維護性 AD 十分 無需 遇到 代碼 必須 有一個 質量高的代碼關鍵之一是具有可維護性和可擴展性。 將面向對象設計,也就是解耦,融入於編碼之中。不要硬編碼,要讓你的代碼擴展起來十分方便。 今天我遇到了一個問題: 面向對象的ISP原則。 接口隔離原則,具體來說就是

11條最全面的C/C++編碼規範總結【轉】

(轉自:https://blog.csdn.net/zang141588761/article/details/50608736) 對於不同的程式語言來說,具體的編碼規範可以有很大的不同,但是其宗旨都是一致的,就是保證程式碼在高質量完成需求的同時具備良好的可讀性、可維護性。例如我們可以

c++ 編碼規範

本人將編碼規範分為兩部分,一個是普通的編碼規範,另一部分為註釋規範,分別採用不同的風格。接下來將分別進行詳細介紹: 編碼規範之Google風格 Google的編碼規範非常優秀,全世界很多人都在採用,筆者也建議採用Google的編碼規範,本文附上Google的編碼規範網址: googl

這套C#編碼規範寫不錯

命名約定 我們在命名識別符號時(包括引數,常量,變數),應使用單詞的首字母大小寫來區分一個識別符號中的多個單詞,如UserName. PascalCasing PascalCasing包含一到多個單詞,每一個單詞第一個字母大寫,其餘字母均小寫。例如:HelloWorl

自己總結的C#編碼規範--2.命名選擇篇

上一篇文章講了識別符號命名的約定,今天講下識別符號命名的選擇。 簡單的講,看到一個識別符號一定要可以見名知意。 名字一定要能夠表達出識別符號的含意 識別符號名字必須要表達出該識別符號的意義,絕對不可以使用無意義的v1,v2…vn之類的命名。 public static void

自己總結的C#編碼規範--3.特定場景下的命名最佳實踐

特定場景下的命名最佳實踐 名稱空間 要使用PascalCasing,並用點號來分隔名字空間中的各個部分。 如Microsof.Office.PowerPoint 要用公司名作為名稱空間的字首,這樣就可以避免與另外一家公司使用相同的名字。 要用穩定的,與版本無關的產

自己總結的C#編碼規範--1.命名約定篇

命名約定 我們在命名識別符號時(包括引數,常量,變數),應使用單詞的首字母大小寫來區分一個識別符號中的多個單詞,如UserName. PascalCasing PascalCasing包含一到多個單詞,每一個單詞第一個字母大寫,其餘字母均小寫。例如:HelloWorld、SetN

C# 編碼規範和程式設計好習慣

誰都會寫程式碼!幾個月的程式設計經驗可以讓你寫出“可執行應用程式”。讓它可執行容易,但是以最有效率的方式編碼就需要下更多的功夫! 要知道,大多數程式設計師在寫“可執行程式碼”,而不是“高效程式碼”。你想成為你們公司”最尊貴的專業人員“嗎?寫”高效程式碼“是一項藝術,你必須學習

C#編碼規範之代碼的增刪改約定

pre 支持 type cal group 一次 情況 delet 約定 C#編碼規範,代碼的增刪改約定: 增加 #region add by Tome 2018-9-20 //原因:描述 int a = GetScale(obj, col); #endregion

整理華為C/C++編碼規範

目  錄 1 排版 2 註釋 3 識別符號命名 4 可讀性 5 變數、結構 6 函式、過程 7 可測性 8 程式效率 9 質量保證 10 程式碼編輯、編譯、審查 11 程式碼測試、維護 12 巨集 1 排版 1-1:程式塊要採用縮排風格編寫,縮排的空格數為4個。 說明:對於

Unity C#編碼規範

1、指令碼的主題、功能、建立者、修改者說明如下所示 其中:Author為指令碼建立者,需要自己修改為自己的名字大寫,比如小明建立了一個名為Ctrl_TrayMove的指令碼,則Author:XM; 如果小李修改小明建立的Ctrl_TrayMove指令碼,則Modify

C++編碼規範(2):命名規範

 如果你程式碼中的變數名,函式數,類名都取得很好,不僅是個有意義的詞或短語,而且確切的表達了該變數或函式的功能.那讀起程式碼來就像看文章一樣,絕對是一種享受.當然理想是美好的,現實可是殘酷的.很少有人能做到這樣.一來嘛你英語詞彙量得多,不樣不容易找到那麼多合適並貼切的

為什麼嵌入式C語言程式設計要有編碼規範

本期和接下來幾期的專題都是與嵌入式C語言編碼規範相關。首先來看兩段有效程式碼完全一樣的程式碼(原始碼取自某晶片的韌體庫,程式碼檢視器選用的UEStudio,檢視配置為顯示空格和Tab)。大家可以感受下,同樣的程式碼,兩種編碼風格比較起來,你看懂程式碼所需要的時間哪個更短。

MISRA C - 嵌入式系統 C 程式設計規範

MISRA C - 嵌入式系統 C 程式設計規範 MISRA C is a set of software development guidelines for the C programming language developed by MISRA (Motor Industry S

【安全開發】C/C++安全編碼規範

C本質上是不安全的程式語言。例如如果不謹慎使用的話,其大多數標準的字串庫函式有可能被用來進行緩衝區攻擊或者格式字串攻擊。但是,由於其靈活性、快速和相對容易掌握,它是一個廣泛使用的程式語言。下面是針對開發安全的C語言程式的一些規範。 1.1.1      緩衝

C語言的編碼規範

個人編碼模板                 —c/c++語言程式設計模板 程式碼總體原則: <1>

【Objective-C】 OC編碼規範

UserModel 的命名方式不太好。Model 是設計模型中的概念,不應該將其命名到具體的程式碼物件中,而且型別必須要有字首,防止命名衝突,此處命名字首取 BDK(Bai Du Knows)。: 和 NSObject 之間要有空格。屬性定義的地方 property / 括號 / 型別 / 變數名 之間應該要

C/C++語言編碼規範

目錄彔彔  目錄2 1.1. 起個合適的名字 ²  類的名稱要能告訴我們,這個類是什麼。因此,類的名稱通常是名詞。 ²  類的名字不需要