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