欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C 语言通用模板队列(宏函数)

程序员文章站 2022-06-16 21:35:47
前言 嵌入式开发过程中,各个模块之间,各个设备之间进行交互时,都会存在数据的输入输出,由于处理的方式不同,数据不会立即同步处理,因此通常在设计时都会设计缓冲区进行数据的处理,方式数据丢失等问题; 一个项目中存在不同模块都需要缓冲区的设计,设计策略基本都一样,不同的是数据结构,在 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编译器插件,可能较慢,稍等