嵌入式開發常用技巧及C/C++知識
- 引言
- static 靜态變量
- ‘##’連接配接符
- 斷言函數
- 宏定義與條件變量
- #if...#else...#endif
- 選擇是否使用序列槽調試
- 其他
- 後續
引言
我們在日常的嵌入式開發中,經常會遇到各種C/C++的使用問題,并且C/C++純軟體的常用開發技巧有些嵌入式并不常用,而嵌入式開發中使用到的C/C++知識與技巧有些也非常特别,這裡我們來具體介紹一下嵌入式開發常用技巧及C/C++知識(未完待續)。
static 靜态變量
靜态全局變量:在全局變量前,加上關鍵字static,該變量就被定義成為一個靜态全局變量。
靜态全局變量有以下特點:
- 該變量在全局資料區配置設定記憶體
- 未經初始化的靜态全局變量會被程式自動初始化為0(在函數體内聲明的自動變量的值是随機的,除非它被顯式初始化,而在函數體外被聲明的自動變量也會被初始化為0)
-
靜态全局變量在聲明它的整個檔案都是可見的,而在檔案之外是不可見的
靜态變量都在全局資料區配置設定記憶體,包括後面将要提到的靜态局部變量。對于一個完整的程式,在記憶體中的分布情況:
代碼區 | low address |
全局資料區堆區棧區 | high address |
一般程式把新産生的動态資料存放在堆區,函數内部的自動變量存放在棧區。自動變量一般會随着函數的退出而釋放空間,靜态資料(即使是函數内部的靜态局部變量)也存放在全局資料區。全局資料區的資料并不會因為函數的退出而釋放空間。
定義全局變量就可以實作變量在檔案中的共享,但定義靜态全局變量還有以下好處:
- 靜态全局變量不能被其它檔案所用
- 其它檔案中可以定義相同名字的變量,不會發生沖突
‘##’連接配接符
##用來連接配接前後兩個參數,把它們變成一個字元串。
例子如下:
#define main(x,y)##y
int xy=1;
cout < < main(x,y) < < endl;
将會使編譯器把
cout < < main(x,y) < < endl;
解釋為
cout < < xy < < endl;
理所當然,将會在标準輸出處顯示’1’。
從此可以看出,x##y的效果就是将x和y連在一起了。
而#define main(x,y) x##y 則相當于把main(x,y)等價于x##y
斷言函數
主要作用:是對一個bool型表達式進行檢查,一個正确運作的程式必須保證這個bool型表達式的值為true,若表達式的值為false,則說明程式已處于一種不正确的狀态下,系統需要提供警告資訊并且退出程式。
在實際開發中assert主要用于保證程式的正确性,通常在程式開發和測試時使用。為了提高運作效率,在軟體釋出後,assert檢查預設是關閉的。
使用斷言的幾個原則:
- 使用斷言捕捉不應該發生的非法情況。不要混淆非法情況與錯誤情況之間的差別,後者是必然存在的并且是一定要作出處理的。
- 使用斷言對函數的參數進行确認。
-
一般教科書都鼓勵程式員們進行防錯性的程式設計,但要記住這種程式設計風格會隐瞞錯誤。當進行防錯性程式設計時,如果"不可能發生"的事情的确發生了,則要使用斷言進行報警。
FreeRTOS 中的 configASSERT
configASSERT是在FreeRTOS中的斷言函數。如果斷言函數的參數為0時将觸發斷言函數的執行。
FreeRTOS的斷言功能在調試階段是非常有用的,可以有效地檢查參數錯誤和運作中的錯誤,但在正式釋出軟體時,請将此功能關閉,因為斷言功能會增加工程代碼大小并降低工程執行效率。關閉斷言也比較簡單,如果FreeRTOSConfig.h檔案中有斷言的宏定義,将其注釋掉即可,如果沒有宏定義,預設在FreeRTOS.h檔案中就是關閉的。
#define configASSERT( x ) if( x == 0 ) {taskDISABLE_INTERRUPTS(); for(;;); }
在使用調試器的情況下,一旦出現斷言失敗,會關閉中斷,程式會死在這個for循環中,此時使用者可以很容易就鎖定函數出錯位置。
宏定義與條件變量
#if…#else…#endif
我們在調試程式時,經常會遇到某段功能的實作,寫了兩種版本的程式,但調試時又不想來回切換。,這時候我們可以使用條件變量。
比如:想測試__set_FAULTMASK(1);和__disable_fault_irq();的差別,就可以使用如下方式,隻需要更改#if後面是1還是0就可以選擇是使用哪段程式。
#if 1
//
__set_FAULTMASK(1);
NVIC_SystemReset();
#else
__disable_irq();
delay_ms(1000);
__disable_fault_irq();
NVIC_SystemReset();
#endif
選擇是否使用序列槽調試
我們在程式開發過程時,往往使用序列槽進行程式的調試,但在産品成熟時,為了避免記憶體開銷和其他的一些問題往往會去掉序列槽調試,這時候應該如何簡單的去掉呢,下面我來介紹一下。
#define DEBUG_EN 1//選擇是否打開printf調試(序列槽一),1代表打開,0代表關閉
#if DEBUG_EN
#define DEBUG(fmt, ...) do{ printf(fmt ,##__VA_ARGS__); }while(0)
#else
#define DEBUG do{ }while(0);
#endif
我們通過上述程式,就可以實作序列槽調試的全部切換,非常友善。
其他
- %*c表示忽略一個字元
- C++變量前面加下劃線和不加下劃線都不會影響對變量的定義,隻是風格問題,更喜歡将成員變量或者私有成員變量的前面加上下劃線。以表示該變量是某個類的屬性。
後續
如果想了解更多物聯網、智能家居項目知識,可以關注我的項目實戰專欄和軟硬結合專欄。
歡迎關注公衆号了解更多。