C语言实现一个小型消息队列模块【可移植】【适合初学者】

C语言实现一个小型消息队列模块【可移植】

本着能有占内存小方便移植使用简单的几个方向,写了这个小型的消息队列,挺适合初学者或者刚工作的人。
我会详细讲解每部分代码。

结构部分

队列结构

使用循环队列,定义一个结构体,里边有数据区头尾指针,最好还是能有个表示数据大小

typedef struct queue_t{
	unsigned short head;
	unsigned short tail;
	char *fifo;
	unsigned short size;
}queue_t;

为了方便获取数据长度,干脆就直接定义了一个变量存储数据长度,而不用那种头尾指针计算方式获取

数据区

推荐使用静态全局变量数组来当数据区,这部分空间不会被回收,同时也不会被其他模块占用。我不推荐在结构体中直接定义数组空间,某些编译器貌似是会优化结构体中的数组空间造成这部分空间与其他变量冲突,而且很容易被其他模块修改,所以我们开辟一个static修饰的静态数组空间,保证这部分空间不会被其他代码访问。

#define QUEUE_NUM	8
#define QUEUE_MAX	256
#if !defined(QUEUE_LIST_SIZE)
#define QUEUE_LIST_SIZE	(sizeof(char)*QUEUE_NUM)
#endif

/* 本模块使用空间 */
static char fifo[QUEUE_LIST_SIZE][QUEUE_MAX];	//数据区
static queue_t queue_fifo[QUEUE_LIST_SIZE];	//队列区
static unsigned char queue_mask;	//mask

预留出几组空间,关于QUEUE_LIST_SIZEQUEUE_MAX两个宏的大小由开发者自己定义,这里我定义8与256,如果修改QUEUE_LIST_SIZE,我建议直接将queue_mask改为unsigned int类型,这里我只是为了方便,开辟了最小能满足的范围。

错误码

这个纯粹是用来方便调试的,看个人喜好来定义

typedef enum QUEUE_ERR{
	QUEUE_ERR_OK = 0,//OK
	QUEUE_ERR_MAXLEN,//超过最大长度
	QUEUE_ERR_OVERLEN,//超过剩余空间长度
	QUEUE_ERR_FULL,//数据满
	QUEUE_ERR_EMPTY,//数据空
	QUEUE_ERR_MEM,//内存失败
	QUEUE_ERR_DATA,//数据错误
}QUEUE_ERR_e;

也不知道以后会不会加新的,目前能用到的也就这些。

接口部分

按照模块化的思想,考虑到能给其他模块使用,最好是能提供以下四个基础的接口:

注册入队列出队列注销
这四个是最基础的。

注册

注册队列当时是从我们之前定义的那几组queue_fifo中获取一个没有使用的。

/**
 * @brief 注册一个队列
 * @param null
 * @return 队列指针
 */
queue_t *queue_init(void)
{
	unsigned char id = 0;
	queue_t *p;
	for(id=0; id<QUEUE_LIST_SIZE; id++)
	{
		if(!((queue_mask>>id)&1))
			break;
	}
	if(id == QUEUE_LIST_SIZE)
	{
		//printf("there is no queue could be alloc\n");
		return NULL;
	}
	queue_mask |= (1<<id);
	p = &queue_fifo[id];
	p->fifo = fifo[id];
	p->head = 0;
	p->tail = 0;
	p->size = 0;
	return p;
}

学过C语言的人我觉得这个应该轻轻松松就能看懂,没学过的我解释下吧,我们之前在队列空间和数据空间的时候不是定义了queue_mask吗,这个就在这里用到了,当作一个掩码来管理队列,注意它是char类型,8个bit位,每一位要么是0,要么是1,这就提供了一个很好的思路,这个队列如果被使用,对应掩码这位就为1,没有使用就是0

所以上边的代码就是注册掩码中最低位的0所在的位置id,用这个来当作队列空间位置,如果为1,我就向右偏移找0所在的位置,如果全都没有,表示队列被注册完了,返回空。所以在调用注册的时候,注意判断是不是空

注销

上边注册的时候我们定义了一个id来获取空闲的队列与数据区,但是这个id是注册接口内部使用的,我们不能在注销的时候使用注册空间内部的数据,有人会疑问:为什么不在外部定义一个id,不好意思,这个id并没有很多经常使用到的地方,所以就不在定义了。

那我们怎么获取id呢,按照注册队列的思路,传入参数我们如果只传一个queue_fifo的指针,我们能知道的就是这个queue_fifofifo这两个地址,他们肯定是存在在上边static区中,通过判断地址,能得到这个id并注销

/**
 * @brief 注销队列
 * @param queue_list 队列
 */
 QUEUE_ERR_e queue_uninit(queue_t *queue_fifo)
{
	unsigned short i=0,j=0;
	if(!queue_fifo || !queue_fifo->fifo)
		return QUEUE_ERR_MEM;
	if(queue_fifo->size != 0)
		return QUEUE_ERR_DATA;
	j = QUEUE_LIST_SIZE - 1;
	while(i<j)
	{
		if(queue_fifo->fifo == fifo[i])
		{
			queue_mask &= ~(1<<i);
			break;
		}
		if(queue_fifo->fifo == fifo[j])
		{
			queue_mask &= ~(1<<j);
			break;
		}
		i++;
		j--;
	}
	queue_fifo->fifo = NULL;
	return QUEUE_ERR_OK;
}

开头的几个判断条件就不说了,我定义了两个指针i,j,这样循环次数还能减少一半,如果发现queue_fifo中fifo的地址与数据区fifo对应的一样,我们就把queue_mask中的那位清0,清零就是我们注销了,然后跳出循环。

为了指针安全考虑,这个queue_fifo中的fifo我们也清空。

入队列

/**
 * @brief 向队列存储数据
 * @param queue 队列
 * @param buff 存储数据
 * @param len 数据长度
 */
QUEUE_ERR_e queue_push(queue_t *queue, char *buff, int len)
{
    if (len > QUEUE_MAX - 1)
        return QUEUE_ERR_MAXLEN;
    if (queue->head != queue->tail)
    {
        if (len > (QUEUE_MAX - queue->size - 1))
            return QUEUE_ERR_FULL;
    }
    for (unsigned short i = 0; i < len; i++)
    {
        queue->tail = (queue->tail + 1) % QUEUE_MAX;
        queue->fifo[queue->tail] = buff[i];
    }
    queue->size += len;
    return QUEUE_ERR_OK;
}

开始进行条件判断,如果入数据长度比队列长度还长就返回超长的错误码。 另一个如果队列中有数据,判断入数据长度与队列剩余空间的长度。如果这两个都通过了,我们可以将这个数据入到队列里。

既然是数组空间,那为了防止溢出,我们要浪费掉一个空间来判断队列是不是满,这个是通用的手法。

入队列就是尾指针向后移动,如果超过队列最大下标,那就从头开始,这样,我们不用担心数据会跑出这个空间。最后返回成功。

出队列

/**
 * @brief 从队列中取数据
 * @param queue 队列
 * @param buff 存储取出数据指针
 * @param len 要取出数据长度
 * @return 真正取出的数据长度
 */
unsigned int queue_pop(queue_t *queue, char *buff, int len)
{
    if (queue->head == queue->tail)
        return QUEUE_ERR_OK;
    unsigned short pop_len = (len > queue->size) ? queue->size : len;
    for (unsigned short i = 0; i < pop_len; i++)
    {
        queue->head = (queue->head + 1) % QUEUE_MAX;
        buff[i] = queue->fifo[queue->head];
    }
    queue->size -= pop_len;
    return pop_len;
}

出队列与入队列差不多,先判断一样有没有数据,如果有数据我们就向下走,取队列中存储的数据长度与要取出的长度,取最小的值,然后按照这个值来循环,依次存入buff中,然后将队列数据大小减掉取出的长度,最后返回取出的实际长度。

这样,这个队列模块就完成了,可移植到内存小的设备中使用。

完整代码

queue.c文件内容

#include "queue.h"

static char fifo[QUEUE_LIST_SIZE][QUEUE_MAX]; // queuedata
static queue_t queue_fifo[QUEUE_LIST_SIZE];    // queuelist
static unsigned char queue_mask;               // MASK

//static void QUEUE_PRI_LOG(queue_t *queue)
//{
//    printf("queue id is [%#x]\n", queue_mask);
//    printf("queue size is [%d]\n", queue->size);
//}
/**
 * @brief 注册一个队列
 * @param null
 * @return 队列指针
 */
queue_t *queue_init(void)
{
    unsigned char id = 0;
    queue_t *p;
    for (id = 0; id < QUEUE_LIST_SIZE; id++)
    {
        if (!((queue_mask >> id) & 1))
            break;
    }
    if (QUEUE_LIST_SIZE == id)
    {
        //printf("there is no queue could be alloc\n");
        return NULL;
    }

    queue_mask |= (1 << id);
    p = &queue_fifo[id];
    p->queue = fifo[id];
    p->head = 0;
    p->tail = 0;
    p->size = 0;
    // QUEUE_PRI_LOG(p_queue);
    return p_queue;
}

/**
 * @brief 向队列存储数据
 * @param queue 队列
 * @param buff 存储数据
 * @param len 数据长度
 */
QUEUE_ERR_e queue_push(queue_t *queue, char *buff, int len)
{
    if (len > QUEUE_MAX - 1)
        return QUEUE_ERR_MAXLEN;
    if (queue->head != queue->tail)
    {
        if (len > (QUEUE_MAX - queue->size - 1))
            return QUEUE_ERR_FULL;
    }
    for (unsigned short i = 0; i < len; i++)
    {
        queue->tail = (queue->tail + 1) % QUEUE_MAX;
        queue->fifo[queue->tail] = buff[i];
    }
    queue->size += len;
    return QUEUE_ERR_OK;
}

/**
 * @brief 从队列中取数据
 * @param queue 队列
 * @param buff 存储取出数据指针
 * @param len 取出数据长度
 */
unsigned short queue_pop(queue_t *queue, char *buff, int len)
{
    if (queue->head == queue->tail)
        return QUEUE_ERR_OK;
    unsigned short pop_len = (len > queue->size) ? queue->size : len;
    for (unsigned short i = 0; i < pop_len; i++)
    {
        queue->head = (queue->head + 1) % QUEUE_MAX;
        buff[i] = queue->fifo[queue->head];
    }
    queue->size -= pop_len;
    return pop_len;
}

/**
 * @brief 注销队列
 * @param queue_list    队列
 */
QUEUE_ERR_e queue_uninit(queue_t *queue_list)
{
    unsigned short i = 0, j = 0;
    if (!queue_list || !queue_list->fifo)
        return QUEUE_ERR_MEM;
    if (queue_list->size != 0)
        return QUEUE_ERR_DATA;
    j = QUEUE_LIST_SIZE - 1;

    while (i < j)
    {
        if (queue_list->fifo == fifo[i])
        {
            queue_mask &= ~(1 << i);
            break;
        }
        if (queue_list->fifo == fifo[j])
        {
            queue_mask &= ~(1 << j);
            break;
        }
        i++;
        j--;
    }
    queue_list->queue = NULL;
    return QUEUE_ERR_OK;
}

#if __TEST__
int main()
{
    /* 测试 */
    queue_t *queue1 = queue_init();
    queue_t *queue2 = queue_init();
    queue_t *queue3 = queue_init();
    queue_t *queue4 = queue_init();
    queue_t *queue5 = queue_init();
    queue_t *queue6 = queue_init();
    queue_t *queue7 = queue_init();
    queue_t *queue8 = queue_init();
    queeu_uninit(queue7);
    queeu_uninit(queue8);
    queue_t *queue9 = queue_init();
    queue_t *queue10 = queue_init();

    char buff1[64] = "hello world";
    char buff2[32] = "123456789";
    char buff_pop1[128] = {0};
    char buff_pop2[128] = {0};
    queue_push(queue9, buff1, sizeof(buff1));
    queue_push(queue10, buff2, sizeof(buff2));
    printf("queue9 size [%d]\n", queue9->size);
    printf("queue10 size [%d]\n", queue10->size);

    queue_pop(queue9, buff_pop1, 5);
    queue_pop(queue10, buff_pop2, 6);
    printf("buff_pop1 is [%s]\n", buff_pop1);
    printf("buff_pop2 is [%s]\n", buff_pop2);
    for (int i = 0; i < sizeof(buff_pop1); i++)
        buff_pop1[i] = 0;
    for (int i = 0; i < sizeof(buff_pop2); i++)
        buff_pop2[i] = 0;
    queue_pop(queue9, buff_pop1, sizeof(buff_pop1));
    queue_pop(queue10, buff_pop2, sizeof(buff_pop2));
    printf("buff_pop1 is [%s]\n", buff_pop1);
    printf("buff_pop2 is [%s]\n", buff_pop2);
    return 0;
#endif

queue.h文件内容

#ifndef QUEUE_H
#define QUEUE_H

#include <stdio.h>

#define QUEUE_NUM   8
#define QUEUE_MAX   256

#if !defined(QUEUE_LIST_SIZE)
#define QUEUE_LIST_SIZE (sizeof(char)*QUEUE_NUM)
#else 
#define QUEUE_LIST_SIZE 8
#endif

typedef struct queue_t{
    unsigned short head;
    unsigned short tail;
    char *fifo;
    unsigned short size;
}queue_t;


typedef enum QUEUE_ERR{
    QUEUE_ERR_OK = 0,   //OK
    QUEUE_ERR_MAXLEN,   //OVER MAX SIZE
    QUEUE_ERR_OVERLEN,  //OVER LEFT SIZE
    QUEUE_ERR_FULL,     //FULL
    QUEUE_ERR_EMPTY,    //EMPTY
    QUEUE_ERR_MEM,
    QUEUE_ERR_DATA,
}QUEUE_ERR_e;

/**
 * @brief 向队列存储数据
 * @param queue 队列
 * @param buff 存储数据
 * @param len 数据长度
 */
QUEUE_ERR_e queue_push(queue_t *queue, char *buff, int len);
/**
 * @brief 从队列中取数据
 * @param queue 队列
 * @param buff 存储取出数据指针
 * @param len 取出数据长度
 */
unsigned short queue_pop(queue_t *queue, char *buff, int len);

/**
 * @brief 注销队列
 * @param queue_list    队列
 */
QUEUE_ERR_e queue_uninit(queue_t *queue_list);

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值