1. 嵌入式队列概述:为什么选择链表实现?
在嵌入式系统开发中,队列是最基础也是最重要的数据结构之一。它遵循先进先出(FIFO)的原则,就像我们日常生活中排队买票一样,先来的人先得到服务。这种特性使得队列特别适合处理需要按顺序处理的数据,比如串口通信中的数据缓存、任务调度中的消息传递等场景。
1.1 数组 vs 链表实现对比
在嵌入式环境下,我们通常有两种实现队列的方式:
-
数组实现:
- 优点:内存连续,访问速度快,缓存命中率高
- 缺点:大小固定,容易产生"假溢出"现象(即数组未满但因循环队列逻辑导致无法入队)
- 适用场景:数据量固定且内存受限的简单应用
-
链表实现:
- 优点:动态内存分配,没有容量限制(直到内存耗尽),内存利用率高
- 缺点:内存不连续,访问速度稍慢,需要额外的指针存储空间
- 适用场景:数据量变化大或不确定的复杂应用
在嵌入式开发中,链表实现通常是更优的选择。原因有三:
- 嵌入式系统经常需要处理不可预测的数据流(如传感器数据)
- 内存资源宝贵,链表可以更灵活地利用碎片化内存
- 现代嵌入式系统的内存管理已经足够成熟,动态分配的代价可以接受
1.2 队列的基本操作
一个完整的队列需要支持以下核心操作:
- 初始化(InitQueue):创建空队列
- 入队(EnQueue):在队尾添加元素
- 出队(DeQueue):从队头移除元素
- 判空(IsEmpty):检查队列是否为空
- 获取队首(GetFront):查看但不移除队首元素
- 销毁(DestroyQueue):释放队列占用的所有内存
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 队列的链表实现详解
2.1 数据结构定义
我们先来看队列的核心数据结构定义。在Queue.h头文件中,我们定义了以下内容:
c复制#ifndef QUEUE_H
#define QUEUE_H
#include <stdio.h>
#include <stdlib.h>
// 队列数据类型定义(可根据需求修改)
typedef int DATATYPE;
// 队列节点结构体
typedef struct QueueNode {
DATATYPE data; // 数据域
struct QueueNode *next; // 指针域
} QueueNode;
// 队列管理结构体
typedef struct Queue {
QueueNode *front; // 队头指针(指向头节点)
QueueNode *rear; // 队尾指针(指向尾节点)
} Queue;
// 函数声明
Queue* InitQueue();
int IsEmpty(Queue *queue);
int EnQueue(Queue *queue, DATATYPE data);
int DeQueue(Queue *queue);
DATATYPE GetFront(Queue* queue);
void PrintQueue(Queue *queue);
void DestroyQueue(Queue *queue);
#endif
这里有几个关键设计点:
- 使用单独的Queue结构体来管理队列,而不是直接操作链表节点
- 采用带头节点的设计,简化边界条件处理
- DATATYPE可以灵活修改,适应不同应用场景
2.2 初始化队列
队列初始化的核心是创建一个头节点,并让front和rear指针都指向它:
c复制Queue* InitQueue()
{
// 分配队列管理结构体
Queue *queue = (Queue*)malloc(sizeof(Queue));
if (queue == NULL) {
perror("队列结构体分配失败");
return NULL;
}
// 创建头节点(不存储有效数据)
QueueNode *head = (QueueNode*)malloc(sizeof(QueueNode));
if (head == NULL) {
perror("头节点分配失败");
free(queue);
return NULL;
}
head->next = NULL;
queue->front = queue->rear = head;
return queue;
}
注意事项:
- 每次malloc后都要检查返回值,嵌入式系统内存有限
- 头节点的next必须初始化为NULL
- 如果头节点分配失败,要记得释放已经分配的队列结构体
2.3 入队操作
入队操作是在队列尾部添加新元素:
c复制int EnQueue(Queue *queue, DATATYPE data)
{
if (queue == NULL || queue->front == NULL || queue->rear == NULL) {
printf("队列未初始化\n");
return 0;
}
// 创建新节点
QueueNode *newNode = (QueueNode*)malloc(sizeof(QueueNode));
if (newNode == NULL) {
perror("新节点分配失败");
return 0;
}
newNode->data = data;
newNode->next = NULL;
// 将新节点链接到队尾
queue->rear->next = newNode;
queue->rear = newNode;
return 1;
}
关键点:
- 新节点的next必须设为NULL
- 要先设置新节点的next指针,再更新rear指针
- 入队操作的时间复杂度是O(1)
2.4 出队操作
出队操作是从队列头部移除元素:
c复制int DeQueue(Queue *queue)
{
if (queue == NULL) {
printf("队列指针为空\n");
return -1;
}
if (IsEmpty(queue)) {
prin
