环形队列
队列是一种先进先出的数据结构,而环形队列则是一个循环利用一个定长空间、从而不浪费空间,并且无需频繁的申请释放内存的一种队列解决方案。具体科普内容请移步百度。
代码实现方案
C语言中有个类型为void,意为无效类型,或者叫空类型,叫啥不重要,重要的是,它的指针可以和任意类型的指针之间互相转换而不会引起编译器报错,此方案就是利用C语言这个特性来实现的任意类型的环形队列。
实现代码
队列结构体
/**
* @brief 队列结构体
*
*/
typedef struct {
uint8_t head;/*队头,出队指针*/
uint8_t tail;/*队尾,入队指针*/
uint8_t size;/*队列最大长度*/
void * data_p;/*队列缓冲池的指针*/
} queue_t;
状态枚举
typedef enum {
QUEUE_SUCCESS = 0,/*成功*/
QUEUE_FAILED,/*失败*/
QUEUE_EMPTY,/*队列空*/
QUEUE_FULL/*队列满*/
} queue_status;
API
/**
* @brief 初始化一个队列
*
* @param Q 队列指针
* @param size 队列大小
* @param data_p 存储数据的数组指针
* @return queue_status
*/
queue_status queue_init(queue_t *Q, uint8_t size, void *data_p);
/**
* @brief 入队
*
* @param Q 队列指针
* @param node 传入数据的指针
* @param size 数据类型字节数
* @return queue_status
*/
queue_status queue_enqueue(queue_t *Q, void * node,uint8_t size);
/**
* @brief 出队
*
* @param Q 队列指针
* @param node 接受数据的指针
* @param size 数据类型字节数
* @return queue_status
*/
queue_status queue_dequeue(queue_t *Q, void *node,uint8_t size);
/**
* @brief 获取队列长度
*
* @param Q 队列句柄
* @return uint8_t
*/
uint8_t queue_length(queue_t Q);
/**
* @brief 清空队列
*
* @param Q 队列句柄
* @return queue_status
*/
queue_status queue_empty(queue_t *Q);
实现过程
/**
* @brief 初始化一个队列
*
* @param Q 队列指针
* @param size 队列大小
* @param data_p 存储数据的数组指针
* @return queue_status
*/
queue_status queue_init(queue_t *Q, uint8_t size, void *data_p) {
if (data_p == NULL)
return QUEUE_FAILED;
Q->head = 0;
Q->tail = 0;
Q->data_p = data_p;
Q->size = size;
return QUEUE_SUCCESS;
}
传入一个缓冲池指针,可以是任意类型,size为队列的最大长度,实际可容纳孔间为size-1个,因为需要有一个空格,否则队头指针和队尾指针会叠加到一块,无法区分是队空还是队满。
/**
* @brief 判断队列满
*
* @param Q 队列句柄
* @return queue_status
*/
queue_status queue_isfull(queue_t Q) {
if (Q.head == (Q.tail + 1) % Q.size) {
return QUEUE_FULL;
}
return QUEUE_SUCCESS;
}
/**
* @brief 判断队列空
*
* @param Q 队列句柄
* @return queue_status
*/
queue_status queue_isempty(queue_t Q) {
if (Q.tail == Q.head) {
return QUEUE_EMPTY;
}
return QUEUE_SUCCESS;
}
判空和判满,因为有个循环问题,所以队尾指针需要对长度取余数,假设队列长度为5,队头队尾指针的范围就是0-4,当队尾指针增加到4的时候,下一个应该是0,(4+1)%5=0。
/**
* @brief 入队
*
* @param Q 队列指针
* @param node 传入数据的指针
* @param size 数据类型字节数
* @return queue_status
*/
queue_status queue_enqueue(queue_t *Q, void *node, uint8_t size) {
/**队满,无法入队*/
if (QUEUE_FULL == queue_isfull(*Q)) {
return QUEUE_FULL;
} //返回错误
memcpy((Q->data_p + (Q->tail * size)), node, size); //存入数据
Q->tail = (Q->tail + 1) % Q->size; //指针后移
return QUEUE_SUCCESS;
}
入队函数,其中重要的就是存入数据那行,根据不同数据类型的长度拷贝不同长度的内存,依赖string.h
,size则是不同数据类型的长度,最好是以sizeof(node)
或者sizeof(type)
的方式传入,避免出错。
/**
* @brief 出队
*
* @param Q 队列指针
* @param node 接受数据的指针
* @param size 数据类型字节数
* @return queue_status
*/
queue_status queue_dequeue(queue_t *Q, void *node, uint8_t size) {
/**队空,无法出队*/
if (QUEUE_EMPTY == queue_isempty(*Q)) {
return QUEUE_EMPTY;
}
memcpy(node, (Q->data_p + (Q->head * size)), size); //取出数据
Q->head = (Q->head + 1) % Q->size;
return QUEUE_SUCCESS;
}
出队函数,与入队函数一样。
/**
* @brief 获取队列长度
*
* @param Q 队列句柄
* @return uint8_t
*/
uint8_t queue_length(queue_t Q) { return (Q.tail + Q.size - Q.head) % Q.size; }
/**
* @brief 清空队列
*
* @param Q 队列句柄
* @return queue_status
*/
queue_status queue_empty(queue_t *Q) {
Q->head = 0;
Q->tail = 0;
return QUEUE_SUCCESS;
}
查看队列当前元素个数以及清空队列函数,此清空队列函数并不会删除缓冲池数据。
以上代码均已上传gitee