對于嵌入式系統來講,嵌入式軟件相當于嵌入式系統的靈魂,整個嵌入式系統如何工作,都是由嵌入式軟件來控制的。如何編寫高質量,高效率的嵌入式軟件在實際項目開發過程中變的越來越重要。
相信大家都有過這樣的感受:看到不規范(雜亂差)的代碼,瞬間就沒有看下去的欲望了。
當我們在公司進行嵌入式項目開發的時候,并不是你一個人在單打獨斗,通常是一個團隊在一起戰斗。很多人一起共同完成一個嵌入式項目,通常是每個成員,每個小組完成整個項目中的一個或幾個模塊。我們編寫的代碼首先是給人看的,其次才是給機器執行的,這就要求我們團隊中的每個人在編寫軟件的時候,要遵循統一的編程規范和編碼風格,提高代碼的可讀性和可維護性,方便團隊成員之間的溝通和交流。在實際項目開發過程中,遵循統一的編程規范相當重要,同學們一定要引起足夠的重視,下面我就從代碼排版,代碼注釋,標識符命名,代碼可讀性和函數設計幾個方面來講解比較通用的嵌入式軟件編程規范。
關于編程規范及原則Ⅱ
編程規范也就是編寫出簡潔、可維護、可靠、可測試、高效、可移植的代碼,提高產品代碼的質量。
本文針對嵌入式,主要結合C語言編程的規范給大家講述。
1.頭文件
對于C語言來說,頭文件的設計體現了大部分的系統設計,不合理的頭文件布局是編譯時間過長的原因。
有很多人將工程中所有的頭文件包含在一個include.h文件中,然后在每一個.c源代碼文件中包含include.h頭文件,這樣做可以讓代碼看上去簡潔,但實際忽視了編譯效率問題,而且代碼的可移植性也不好。
原則:
A.頭文件中適合放置接口的聲明,不適合放置實現;
B.頭文件應當職責單一;
C.頭文件應向穩定的方向包含。
規則:
A.每一個.c文件應有一個同名.h文件,用于聲明需要對外公開的接口;
B.禁止頭文件循環依賴;
C..c/.h文件禁止包含用不到的頭文件;
D.頭文件應當自包含;
E.總是編寫內部#include保護符(#define保護);
F.禁止在頭文件中定義變量;
G.只能通過包含頭文件的方式使用其他.c提供的接口,禁止在.c中通過extern的方式使用外部函數接口、變量;
H.禁止在extern "C"中包含頭文件。
建議:
A.一個模塊通常包含多個.c文件,建議放在同一個目錄下,目錄名即為模塊名。為方便外部使用者,建議每一個模塊提供一個.h,文件名為目錄名;
B.如果一個模塊包含多個子模塊,則建議每一個子模塊提供一個對外的.h,文件名為子模塊名(降低接口使用者的編寫難度);
C.頭文件不要使用非習慣用法的擴展名,如.inc;
D.同一產品統一包含頭文件排列方式。
2.函數
函數設計的要點:編寫整潔的函數,同時把代碼有效組織起來。
函數整潔的要求:代碼簡單直接、不隱藏設計者的意圖、用干凈利落的抽象和直截了當的控制語句將函數有機組織起來。
原則:
A.一個函數僅完成一件功能;
B.重復代碼應該盡可能提煉成函數.
規則:
A.避免函數過長,新增函數不超過100行(非空非注釋行);
B.避免函數的代碼塊嵌套過深,新增函數的代碼塊嵌套不超過4層;
C.可重入函數應避免使用共享變量;若需要使用,則應通過互斥手段(關中斷、信號量)對其加以保護;
D.對參數的合法性檢查,由調用者負責還是由接口函數負責,應在項目組/模塊內應統一規定;
E.對函數的錯誤返回碼要全面處理;
F.設計高扇入,合理扇出(小于7)的函數;
G.廢棄代碼(沒有被調用的函數和變量)要及時清除。
建議:
A.函數不變參數使用const;
B.函數應避免使用全局變量、靜態局部變量和I/O操作,不可避免的地方應集中使用;
C.檢查函數所有非參數輸入的有效性,如數據文件、公共變量等;
D.函數的參數個數不超過5個;
E.除打印類函數外,不要使用可變長參函數;
F.在源文件范圍內聲明和定義的所有函數,除非外部可見,否則應該增加static關鍵字。
3.標識符命名與定義
程序命名是一個關鍵,如果命名不規范,自己寫的代碼,時間長了恐怕連自己都不知道是什么意思了。
3.1通用命名規則
常見命名風格:
A.用下劃線?_?分割,如text_mutex;
規則:
A.標識符的命名要清晰、明了,有明確含義,同時使用完整的單詞或大家基本可以理解的縮寫,避免使人產生誤解;
B.除了常見的通用縮寫以外,不使用單詞縮寫,不得使用漢語拼音;
C.產品/項目組內部應保持統一的命名風格.
建議:
A.用正確的反義詞組命名具有互斥意義的變量或相反動作的函數等;
B.盡量避免名字中出現數字編號,除非邏輯上的確需要編號;
C.標識符前不應添加模塊、項目、產品、部門的名稱作為前綴;
D.平臺/驅動等適配代碼的標識符命名風格保持和平臺/驅動一致;
E.重構/修改部分代碼時,應保持和原有代碼的命名風格一致。
3.2文件命名規則
因為不同系統對文件名大小寫處理會不同,建議文件命名統一采用小寫字符。
3.3變量命名規則
首先,全局變量十分危險,通過前綴使得全局變量更加醒目,促使開發人員對這些變量的使用更加小心。
其次,從根本上說,應當盡量不使用全局變量,增加g_和s_前綴,會使得全局變量的名字顯得很丑陋,從而促使開發人員盡量少使用全局變量。
規則:
A.全局變量增加“g_”前綴,靜態變量增加“s_”前綴;
B.禁止使用單字節命名變量,但允許定義i、j、k作為局部循環變量;
C.使用名詞或者形容詞+名詞方式命名變量。
3.4函數命名規則
A.函數命名應以函數要執行的動作命名,一般采用動詞或者動詞+名詞的結構;
B.函數指針除了前綴,其他按照函數的命名規則命名。
3.5宏的命名規則
A.對于數值或者字符串等等常量的定義,建議采用全大寫字母,單詞之間加下劃線?_?的方式命名(枚舉同樣建議使用此方式定義);
B.除了頭文件或編譯開關等特殊標識定義,宏定義不能使用下劃線?_?開頭和結尾。
4.變量
原則:
A.一個變量只有一個功能,不能把一個變量用作多種用途;
B.結構功能單一;不要設計面面俱到的數據結構;
C.不用或者少用全局變量。
規則:
A.防止局部變量與全局變量同名;
B.通訊過程中使用的結構,必須注意字節序;
C.嚴禁使用未經初始化的變量作為右值;
建議:
A.構造僅有一個模塊或函數可以修改、創建,而其余有關模塊或函數只訪問的全局變量,防止多個不同模塊或函數都可以修改、創建同一全局變量的現象;
B.使用面向接口編程思想,通過API訪問數據:如果本模塊的數據需要對外部模塊開放,應提供接口函數來設置、獲取,同時注意全局數據的訪問互斥;
C.在首次使用前初始化變量,初始化的地方離使用的地方越近越好;
D.明確全局變量的初始化順序,避免跨模塊的初始化依賴;
E.盡量減少沒有必要的數據類型默認轉換與強制轉換。
5.宏、常量
因為宏只是簡單的代碼替換,不會像函數一樣先將參數計算后,再傳遞。
規則:
A.用宏定義表達式時,要使用完備的括號;
不規范:#defineRECTANGLE_AREA(a, b) a * b
規范:#defineRECTANGLE_AREA(a, b) ((a) * (b))
B.將宏所定義的多條表達式放在大括號中;
C.使用宏時,不允許參數發生變化;
#define SQUARE(a) ((a) * (a))
int a = 5;
int b;
不規范:
b = SQUARE(a++);
規范:
b = SQUARE(a);
a++;
建議:
A.除非必要,應盡可能使用函數代替宏;
B.常量建議使用const定義代替宏;
C.宏定義中盡量不使用return、goto、continue、break等改變程序流程的語句。
6.注釋
原則:
A.優秀的代碼可以自我解釋,不通過注釋即可輕易讀懂;
B.注釋的內容要清楚、明了,含義準確,防止注釋二義性;
C.在代碼的功能、意圖層次上進行注釋,即注釋解釋代碼難以直接表達的意圖,而不是重復描述代碼。
規則:
A.修改代碼時,維護代碼周邊的所有注釋,以保證注釋與代碼的一致性。不再有用的注釋要刪;
B.文件頭部應進行注釋,注釋必須列出:版權說明、版本號、生成日期、作者姓名、工號、內容、功能說明、與其它文件的關系、修改日志等,頭文件的注釋中還應有函數功能簡要說明;
C.函數聲明處注釋描述函數功能、性能及用法,包括輸入和輸出參數、函數返回值、可重入的要求等;定義處詳細描述函數功能和實現要點,如實現的簡要步驟、實現的理由、設計約束等;
D.全局變量要有較詳細的注釋,包括對其功能、取值范圍以及存取時注意事項等的說明;
E.注釋應放在其代碼上方相鄰位置或右方,不可放在下面。如放于上方則需與其上面的代碼用空行隔開,且與下方代碼縮進相同;
F.避免在注釋中使用縮寫,除非是業界通用或子系統內標準化的縮寫;
G.同一產品或項目組統一注釋風格。
建議:
A.避免在一行代碼或表達式的中間插入注釋;
B.文件頭、函數頭、全局常量變量、類型定義的注釋格式采用工具可識別的格式。
7.排版與格式
規則:
A.程序塊采用縮進風格編寫,每級縮進為4個空格;
B.相對獨立的程序塊之間、變量說明之后必須加空行;
C.一條語句不能過長,如不能拆分需要分行寫。一行到底多少字符換行比較合適,產品可以自行確定;
D.多個短語句(包括賦值語句)不允許寫在同一行內,即一行只寫一條語句;
E.if、for、do、while、case、switch、default等語句獨占一行;
F.在兩個以上的關鍵字、變量、常量進行對等操作時,它們之間的操作符之前、之后或者前后要加空格;進行非對等操作時,如果是關系密切的立即操作符(如->),后不應加空格;
G.注釋符(包括?/*??//??*/?)與注釋內容之間要用一個空格進行分隔。
嵌入式編程中的注意事項
嵌入式軟件開發和普通軟件編程相比,有一些自己的特點,下面從嵌入式軟件架構,中斷編程,寄存器配置,浮點運算等幾個方面來講解嵌入式編程中的注意事項.
1. 嵌入式系統的軟件架構
一個大型的嵌入式軟件往往需要根據功能的不同劃分成多個軟件功能模塊。
1) 模塊即是一個.c文件和一個.h文件的結合,頭文件(.h)中是對于該模塊接口的聲明;
2) 某模塊提供給其它模塊調用的外部函數及數據需在.h中文件中冠以extern關鍵字聲明;
3) 模塊內的函數和全局變量需在.c文件開頭冠以static關鍵字聲明;
4) 永遠不要在.h文件中定義變量!定義變量和聲明變量的區別在于定義會產生內存分配的操作,是匯編階段的概念;而聲明則只是告訴包含該聲明的模塊在連接階段從其它模塊尋找外部函數和變量
2. 中斷編程
中斷是嵌入式系統中重要的組成部分,但是在標準C中不包含中斷。 許多編譯開發商在標準C上增加了對中斷的支持,提供新的關鍵字用于標示中斷服務程序. 類似于__interrupt、#program interrupt等。當一個函數被定義為中斷服務處理程序的時候,編譯器會自動為該函數增加中斷服務程序所需要的中斷現場入棧和出棧代碼。
中斷服務程序需要滿足如下要求:
1) 不能有返回值;
2) 不能向中斷服務處理程序傳遞參數;
3) 中斷服務處理程序應該盡可能的短小精悍,不要包含耗時的代碼
3. 寄存器配置
嵌入式軟件是面向硬件底層的軟件,我們在對硬件進行編程時,通常是通過配置硬件相關的寄存器來實現的。在配置寄存器時,通常我們只需要配置寄存器的1位或幾位,對于其他不需要配置的位,我們要保持不變,不要更改我們不需要配置的位。
例如:我們希望配置寄存器的GPIOADAT 的第 1位 為 1
我們不能這樣寫成這樣:
GPIOADAT = 0x02; //將其他位設置為 0
而應該寫成這樣:
GPIOADAT |= 0x02; //保證其他位不變
4. 浮點運算
大多數低檔次的單片機都是不支持浮點運算的,因此在實際使用過程中也很少用到,因此為了降低成本,一般都去掉了浮點運算模塊,這就帶來了一個問題,如果萬一要用到浮點運算怎么辦?我們可以采用的是“定點”的方法來解決這個問題,就是直接放大10的N次方倍進行整數的計算,可以得出近似值,因此為了不增加不必要的麻煩,應該總是盡量避免使用浮點運算,一般情況也是可以避免的。
5. volatile 關鍵字的使用
嵌入式開發過程中,在定義硬件寄存器的時候,需要使用volatile關鍵字。 volatile提醒編譯器它后面所定義的變量隨時都有可能改變,因此編譯后的程序每次需要存儲或讀取這個變量的時候,都會直接從變量地址中讀取數據。 如果沒有volatile關鍵字,則編譯器可能優化讀取和存儲,可能暫時使用寄存器中的值。
例如: #define GPIO_DATA (*(volatile unsigned int *)0x90002000)
-
嵌入式
+關注
關注
5082文章
19126瀏覽量
305201 -
C語言
+關注
關注
180文章
7604瀏覽量
136827 -
編程
+關注
關注
88文章
3616瀏覽量
93735
原文標題:嵌入式軟件開發編程規范你了解嗎?小心吃大虧
文章出處:【微信號:wujianying_danpianji,微信公眾號:單片機精講吳鑒鷹】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論