最佳實(shí)踐和行業(yè)標(biāo)準(zhǔn)隨著時(shí)間的推移而發(fā)展和演變,但它們代表了指導(dǎo)智慧的快照。盡管技術(shù)進(jìn)步使先前已知的最佳實(shí)踐無效,但最佳實(shí)踐可能發(fā)展緩慢,并且經(jīng)常變得根深蒂固。在嵌入式開發(fā)領(lǐng)域使用 C 語(yǔ)言特征也遭遇了同樣的命運(yùn)。許多最佳實(shí)踐起源于 80 年代和 90 年代,當(dāng)時(shí)編譯器還很古怪,而微控制器確實(shí)資源有限。從那時(shí)起,編譯器和微控制器已經(jīng)取得了長(zhǎng)足的進(jìn)步,許多“被禁止”的特征和設(shè)計(jì)技術(shù)可能不再如此。
特征1 – Float
在嵌入式系統(tǒng)中使用Float長(zhǎng)期以來一直受到強(qiáng)烈反對(duì)。Float 傳統(tǒng)上存在許多潛在的問題來源。首先,微控制器沒有浮點(diǎn)單元。在大多數(shù)情況下,對(duì)于最低端的微控制器,這種說法在今天仍然適用,但包含浮點(diǎn)單元 (FPU) 的成本已大幅下降到中檔和通用微控制器開始包含 FPU 的程度。微控制器技術(shù)的進(jìn)步現(xiàn)在還包括數(shù)學(xué)函數(shù)的硬件加速,即使沒有FPU也可以幫助加快計(jì)算速度。
其次,在沒有 FPU 的情況下使用float需要編譯器引入龐大而緩慢的軟件庫(kù)。自20世紀(jì)后期以來,編譯器技術(shù)得到了極大的改進(jìn),即使在軟件中的8位微控制器上執(zhí)行浮點(diǎn)計(jì)算也被優(yōu)化到可以忽略不計(jì)的程度。技術(shù)正在發(fā)生變化,嵌入式開發(fā)人員需要隨之改變。
特征2–malloc
Malloc允許開發(fā)人員在程序執(zhí)行期間動(dòng)態(tài)分配內(nèi)存,如果在嵌入式系統(tǒng)中使用不當(dāng),它可能是一個(gè)非常危險(xiǎn)的工具。傳統(tǒng)上,在資源受限的系統(tǒng)中完全禁止使用malloc,這是有原因的。當(dāng)堆變得支離破碎時(shí)會(huì)發(fā)生什么?開發(fā)人員如何處理內(nèi)存分配失敗?內(nèi)存泄漏怎么辦?這些都是困難的程序,需要代碼空間和馬力來處理,傳統(tǒng)的微控制器無法處理。
微控制器不再“傳統(tǒng)”。2015年,低成本微控制器的時(shí)鐘速度可能超過200 MHz,閃存空間超過1 MB,RAM高達(dá)64 KB(高達(dá)256 MB)。即使在一個(gè)不那么強(qiáng)大的系統(tǒng)中,也有正確實(shí)現(xiàn)和使用malloc的工具。應(yīng)用程序可能也很好地保證了這一點(diǎn),因此不應(yīng)該僅僅因?yàn)檫^時(shí)的最佳實(shí)踐就從一開始就將其排除在考慮之外。
特征3 – printf
一般來說,嵌入式開發(fā)人員在嵌入式系統(tǒng)中使用 C 庫(kù)函數(shù)被認(rèn)為是不好的做法。大多數(shù) C 庫(kù)不是可重入的,通常體積龐大且執(zhí)行緩慢(或者是嗎?),或者以阻塞執(zhí)行直到完成的方式實(shí)現(xiàn)。printf的使用屬于這些類別中的許多類別,并且已避免在嵌入式系統(tǒng)中使用。
如前所述,在編譯器優(yōu)化和硬件改進(jìn)之間發(fā)生了很多變化,使用printf是過去的罪惡感。考慮到典型的32 kB閃存空間,該函數(shù)雖然被認(rèn)為是“大”,但占用的代碼空間很小。典型的實(shí)現(xiàn)將printf用作阻塞函數(shù),這會(huì)影響實(shí)時(shí)響應(yīng)并占用潛在的共享資源。通過將printf鏈接到循環(huán)緩沖區(qū)和中斷驅(qū)動(dòng)程序傳輸驅(qū)動(dòng)程序,可以輕松克服響應(yīng)問題,從而允許程序執(zhí)行繼續(xù),同時(shí)驅(qū)動(dòng)程序執(zhí)行必要的工作。
特征4 – memset
大多數(shù)涉及內(nèi)存操作或動(dòng)態(tài)內(nèi)存的 C 特征最終都在禁止中。原因很明顯,許多嵌入式開發(fā)人員和團(tuán)隊(duì)在過去使用這樣的特征時(shí)都吃了虧。當(dāng)諸如 malloc 或 memset 之類的功能最有意義時(shí),盡管不應(yīng)回避它們。相反,開發(fā)人員應(yīng)該確保他們完全了解如何使用該功能,正確使用需要哪些預(yù)防措施,在最壞的情況發(fā)生時(shí)如何恢復(fù),當(dāng)然還有適當(dāng)?shù)臏y(cè)試以確保一切按計(jì)劃進(jìn)行。
特征5 – C位字段
一個(gè)概念是使用在C標(biāo)準(zhǔn)中定義模糊的特征,這個(gè)概念的使用并不植根于編譯器技術(shù)的進(jìn)步或硬件的進(jìn)步。這種特征的一個(gè)很好的例子是位字段。使用位字段的最大問題是通常存在可移植性問題。一個(gè)簡(jiǎn)單的例子是,位的排序不是標(biāo)準(zhǔn)的,編譯器通常可以重新排列這些位,使之成為最有效的實(shí)現(xiàn)方式。向整體結(jié)構(gòu)中添加填充字節(jié)也有問題。
一般的經(jīng)驗(yàn)法則是避免使用位字段,但是有很多情況下它們是有意義的,甚至可移植性問題也無關(guān)緊要。使用位字段的一個(gè)很好的例子是創(chuàng)建一個(gè)結(jié)構(gòu),該結(jié)構(gòu)用于創(chuàng)建初始化驅(qū)動(dòng)程序或應(yīng)用程序的配置表。通過寫入初始化來讀取位的值,重新排序甚至填充字節(jié)都不是問題。
結(jié)論
標(biāo)準(zhǔn)和最佳實(shí)踐旨在幫助開發(fā)人員避免搬起石頭砸自己的腳,但它們僅僅是最佳實(shí)踐。一個(gè)標(biāo)準(zhǔn)可能會(huì)說禁止使用C特征,但事實(shí)是,這取決于開發(fā)人員在他自己獨(dú)特的情況下決定傳統(tǒng)智慧是否適用。挑戰(zhàn)我們的先入之見,并確保我們理解為什么那些最佳實(shí)踐是適當(dāng)?shù)模@與遵循它們同樣重要。不要在不了解它們的應(yīng)用、風(fēng)險(xiǎn)和回報(bào)的情況下就放棄它們。嵌入式開發(fā)人員在考慮使用這些“禁止”的特征時(shí),需要了解它們的時(shí)間、性能和大小限制。