天天看點

嵌入式開發常用技巧及C/C++知識

嵌入式開發常用技巧及C/C++知識

  • ​​引言​​
  • ​​static 靜态變量​​
  • ​​‘##’連接配接符​​
  • ​​斷言函數​​
  • ​​宏定義與條件變量​​
  • ​​#if...#else...#endif​​
  • ​​選擇是否使用序列槽調試​​
  • ​​其他​​
  • ​​後續​​

引言

我們在日常的嵌入式開發中,經常會遇到各種C/C++的使用問題,并且C/C++純軟體的常用開發技巧有些嵌入式并不常用,而嵌入式開發中使用到的C/C++知識與技巧有些也非常特别,這裡我們來具體介紹一下嵌入式開發常用技巧及C/C++知識(未完待續)。

static 靜态變量

靜态全局變量:在全局變量前,加上關鍵字static,該變量就被定義成為一個靜态全局變量。

靜态全局變量有以下特點:

  1. 該變量在全局資料區配置設定記憶體
  2. 未經初始化的靜态全局變量會被程式自動初始化為0(在函數體内聲明的自動變量的值是随機的,除非它被顯式初始化,而在函數體外被聲明的自動變量也會被初始化為0)
  3. 靜态全局變量在聲明它的整個檔案都是可見的,而在檔案之外是不可見的

    靜态變量都在全局資料區配置設定記憶體,包括後面将要提到的靜态局部變量。對于一個完整的程式,在記憶體中的分布情況:

代碼區 low address
全局資料區堆區棧區 high address

一般程式把新産生的動态資料存放在堆區,函數内部的自動變量存放在棧區。自動變量一般會随着函數的退出而釋放空間,靜态資料(即使是函數内部的靜态局部變量)也存放在全局資料區。全局資料區的資料并不會因為函數的退出而釋放空間。

定義全局變量就可以實作變量在檔案中的共享,但定義靜态全局變量還有以下好處:

  1. 靜态全局變量不能被其它檔案所用
  2. 其它檔案中可以定義相同名字的變量,不會發生沖突

‘##’連接配接符

##用來連接配接前後兩個參數,把它們變成一個字元串。

例子如下:

#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檢查預設是關閉的。

使用斷言的幾個原則:

  1. 使用斷言捕捉不應該發生的非法情況。不要混淆非法情況與錯誤情況之間的差別,後者是必然存在的并且是一定要作出處理的。
  2. 使用斷言對函數的參數進行确認。
  3. 一般教科書都鼓勵程式員們進行防錯性的程式設計,但要記住這種程式設計風格會隐瞞錯誤。當進行防錯性程式設計時,如果"不可能發生"的事情的确發生了,則要使用斷言進行報警。

    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      

我們通過上述程式,就可以實作序列槽調試的全部切換,非常友善。

其他

  1. %*c表示忽略一個字元
  2. C++變量前面加下劃線和不加下劃線都不會影響對變量的定義,隻是風格問題,更喜歡将成員變量或者私有成員變量的前面加上下劃線。以表示該變量是某個類的屬性。

後續

如果想了解更多物聯網、智能家居項目知識,可以關注我的​​項目實戰​​​專欄和​​軟硬結合​​專欄。

歡迎關注公衆号了解更多。

繼續閱讀