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编译器插件,可能较慢,稍等

借用一句话来说“代码是写给人看的,不是写给机器看的,只是顺便计算机可以执行而已”
posted @ 2021-06-04 11:33  大橙子疯  阅读(56)  评论(0编辑  收藏  举报