嵌入式 c語言基礎注意事項

2020-08-13 13:41:34

Volatile

volatile易變的,可變的,一般用於以下幾種狀況:

1)並行裝置的硬體暫存器(如:狀態暫存器)

2)一箇中斷服務子程式中會存取到的非自動變數(Non-automatic variables)

3)多執行緒應用中被幾個任務共用的變數

volatile可以解決使用者模式和異常中斷存取同一個變數時,出現的不同步問題,另外在存取硬體地址時,volatile也阻止對地址存取的優化,從而確保存取的實際的地址,精通volatile的運用,在嵌入式底層中十分重要,也是嵌入式C從業者的基本要求之一。函數指針在一般嵌入式軟體的開發中並不常見,但對許多重要的實現如非同步回撥,驅動模組,使用函數指針就可以利用簡單的方式實現很多應用

 

C語言的結構體支援指針和變數的方式存取,通過轉換可以解析任意記憶體的數據(如我們之前提到的通過指針強制轉換解析協定),另外通過將數據和函數指針打包,在通過指針傳遞,是實現驅動層實介面切換的重要基礎,有着重要的實踐意義,另外基於位域,聯合體,結構體,可以實現另一種位元運算,這對於封裝底層硬體暫存器具有重要意義,實踐如下:

typedef unsigned char uint8_t; 
  union reg{     
    struct{         
    uint8_t bit0:1;         
    uint8_t bit1:1;         
    uint8_t bit2_6:5;         
    uint8_t bit7:1;     
  }bit;     
  uint8_t all; 
}; 

int main(void)
{     
  union reg RegData;     
  RegData.all = 0;      
  RegData.bit.bit0 = 1;     
  RegData.bit.bit7 = 1;     
  printf("0x%x\n", RegData.all);      
  RegData.bit.bit2_6 = 0x3;     
  printf("0x%x\n", RegData.all); 
} 
/* 
0x81 
0x8d
*/

通過聯合體和位域操作,可以實現對數據內bit的存取,這在暫存器以及記憶體受限的平臺,提供了簡便且直觀的處理方式,另外對於結構體的另一個重要知識點就是對齊了,通過對齊存取,可以大幅度提高執行效率,但是因爲對齊引入的儲存長度問題,也是容易出錯的問題,對於對齊的理解,可以分類爲如下說明。

基礎數據型別:以預設的的長度對齊,如char以1位元組對齊,short以2位元組對齊等

陣列 :按照基本數據型別對齊,第一個對齊了後面的自然也就對齊了。

聯合體 :按其包含的長度最大的數據型別對齊。

結構體:結構體中每個數據型別都要對齊,結構體本身以內部最大數據型別長度對齊

 

預處理

#if..#elif...#else...#endif, #ifdef..#endif, #ifndef...#endif條件選擇判斷,條件選擇主要用於切換程式碼塊,這種綜合性專案和跨平臺專案中爲了滿足多種情況下的需求往往會被使用。

#undef 取消定義的參數,避免重定義問題。

#error,#warning用於使用者自定義的告警資訊,配合#if,#ifdef使用,可以限制錯誤的預定義設定。

#pragma 帶參數的預定義處理,常見的#pragma pack(1), 不過使用後會導致後續的整個檔案都以設定的位元組對齊,配合push和pop可以解決這種問題,程式碼如下:

#pragma pack(push)
#pragma pack(1)
struct TestA
{
   char i;
   int b;
}A;
#pragma pack(pop); //注意要呼叫pop,否則會導致後續檔案都以pack定義值對齊,執行不符合預期
等同於
 struct _TestB{  
   char i;
   int b;
 }__attribute__((packed))A;