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