C 語言通用模板隊列(宏函數)


前言

       嵌入式開發過程中,各個模塊之間,各個設備之間進行交互時,都會存在數據的輸入輸出,由於處理的方式不同,數據不會立即同步處理,因此通常在設計時都會設計緩沖區進行數據的處理,方式數據丟失等問題;

       一個項目中存在不同模塊都需要緩沖區的設計,設計策略基本都一樣,不同的是數據結構,在 C 語言中可以編寫緩沖區功能函數,入參類型通常為無類型指針,適配所有需要儲存的不同數據結構,但是這種方式必須先知道不同數據結構體的大小,在寫入和讀取時按一個個字節操作。

       下面介紹的是使用宏定義函數實現該方式,按照數據結構的形式賦值速度快,效率高,但是需要一定內存(宏定義替換),以空間換時間。


背景

之前我寫的數據隊列功能函數,有兩種實現方式。

固定類型指針入參

隊列的入參類型為固定類型指針,如: QueuePushData(TestInfo_t *pQueueBuf, TestInfo_t *pSrcData, QueueCtrl *pCtrl)。

優點是數據寫入/讀取效率高(類型的大小內存拷貝),缺點是函數功能不能復用

無類型指針入參

隊列的入參類型為無類型指針,如: QueuePushData(void *pQueueBuf, void *pSrcData, QueueCtrl *pCtrl)。

優點是函數功能可復用(無類型編譯不報錯),缺點是數據寫入/讀取效率和固定類型比較低(通過單字節或 memcpy() 等方式拷貝),且隊列控制中需要增加隊列數據的占用大小


實現方式

為了達到數據寫入/讀取效率高函數功能可復用兩種優點,通過宏定義函數的方式實現(相對函數來說,占用代碼空間,內存足夠的情況下目的是以空間換時間)。

宏定義函數實現數據隊列的功能,適用不同數據結構,類似於 C++ 的模板方式,相同的實現邏輯,不同的數據結構。

結構體定義

首先對需要定義一個數據隊列的控制句柄和一些控制狀態掩碼

點擊查看代碼
/**
  * @brief  緩存區操作信息結構體定義
  */
typedef struct{
    uint8_t  state;         /*!< 控制狀態 */
    
    uint8_t  end;           /*!< 循環隊列尾哨兵 */
    
    uint8_t  head;          /*!< 循環隊列首哨兵 */
    
    uint8_t  num;           /*!< 循環隊列中能存儲的最多組數 */
} QueueCtrl_t;

#define QUEUE_ENABLE_COVER      (0X80)
#define QUEUE_EXIT_DATA         (0X01)
#define QUEUE_DATA_FULL         (0X02)
#define QUEUE_DATA_LOCK         (0X04)

隊列的初始化

定義數據隊列的控制句柄,需要初始化這個句柄,之后才能正常操作隊列。

點擊查看代碼
/**
  * @brief      隊列控制初始化
  * 
  * @param[in,out]  ctrl - 隊列控制句柄
  * @param[in]  num - 隊列數目大小
  * @param[in]  cover - 0,不覆蓋; 1,隊列滿了覆蓋頂端數據
  */
#define QUEUE_INIT(ctrl, maxNum, cover)  ({\
    ctrl.end    = 0;\
    ctrl.head   = 0;\
    ctrl.num    = (maxNum);\
    ctrl.state  = 0x00;\
    ctrl.state  |= ((cover) ? QUEUE_ENABLE_COVER : 0);\
})

隊列的存放

初始化完后,則可以寫入數據至隊列當中

點擊查看代碼
/**
  * @brief      在隊列末尾加入新的數據
  * 
  * @param[in,out]  dstLists - 隊列緩存區
  * @param[in]      src - 新的數據
  * @param[in,out]  ctrl - 隊列控制句柄
  * @retval     返回的值含義如下
  *             @arg 0: 寫入成功
  *             @arg -1: 寫入失敗
  */
#define QUEUE_PUSH_DATA(dstLists, src, ctrl)  ({ \
    int ret = 0;\
\
    if (QUEUE_DATA_LOCK != ((ctrl.state) & QUEUE_DATA_LOCK))  \
    {\
        dstLists[(ctrl.end)++] = src;\
        (ctrl.state) |= QUEUE_EXIT_DATA;  \
\
        if ((ctrl.end) >= (ctrl.num))\
        {\
            (ctrl.end) = 0;\
        }\
\
        if (((ctrl.state) & QUEUE_DATA_FULL) == QUEUE_DATA_FULL)\
        {\
            (ctrl.head) = (ctrl.end);\
        }\
        else if ((ctrl.end) == (ctrl.head))\
        {\
            (ctrl.state) |= QUEUE_DATA_FULL;\
\
            if ((ctrl.state & QUEUE_ENABLE_COVER) != QUEUE_ENABLE_COVER)     \
            {\
                (ctrl.state) |= QUEUE_DATA_LOCK;\
            }\
        }\
\
        ret = 0;\
    }\
    else\
    {\
        ret = -1;\
    }\
\
    ret;\
})

隊列的讀取

點擊查看代碼
/**
  * @brief      在隊列頂端讀取數據
  * 
  * @param[in,out]  dstLists - 隊列緩存區
  * @param[out]     dst - 讀取的數據
  * @param[in,out]  ctrl - 隊列控制句柄
  * @retval     返回的值含義如下
  *             @arg 0: 讀取成功
  *             @arg -1: 讀取失敗
  */
#define QUEUE_POP_DATA(dstLists, dst, ctrl)  ({\
\
    int ret = -1;\
\
    if (((ctrl.state) & QUEUE_EXIT_DATA) == QUEUE_EXIT_DATA)\
    {\
        dst = dstLists[ctrl.head++];\
\
        if ((ctrl.head) >= (ctrl.num))\
        {\
            ctrl.head = 0;\
        }\
\
        if ((ctrl.head) == (ctrl.end))\
        {\
            if (((ctrl.state) & QUEUE_DATA_FULL) != QUEUE_DATA_FULL)\
            {\
                (ctrl.state) &= ~QUEUE_EXIT_DATA;\
            }\
        }\
\
        ret = 0;\
    }\
\
    (ctrl.state) &= ~QUEUE_DATA_LOCK;\
    (ctrl.state) &= ~QUEUE_DATA_FULL;\
\
    ret;\
})

Demo測試代碼

正在加載Demo編譯器插件,可能較慢,稍等


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM