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