循环数组队列需区分逻辑空与满,因front==rear既可表空也可表满;常用方案有牺牲一单元(队满条件为(rear+1)%capacity==front)或记录元素个数(引入size成员)。

循环数组队列本身就能防止假溢出,关键在怎么写判空/判满逻辑——不加处理时 front == rear 既可能是空,也可能是满,必须打破这个歧义。
为什么 front == rear 不能直接用来判断队满
普通顺序队列里,front == rear 确实只表示空;但循环后,rear 绕一圈追上 front,也会出现这个等式。比如容量为 5 的数组,入队 5 次后:front = 0,rear = 0(因为 (0 + 5) % 5 == 0),此时队列已满,却和初始空状态完全一样。
常见错误现象:队列明明还有空间,enqueue() 却始终返回失败;或者反过来,队列已空,dequeue() 还能取到脏数据。
- 根本原因是没区分“逻辑空”和“逻辑满”的指针关系
- 不引入额外信息,仅靠两个指针无法唯一确定状态
- C++ 中若用
std::vector动态扩容,反而掩盖问题——但那已不是循环队列本意
两种主流方案:牺牲一个单元 vs 记录元素个数
工业代码里基本就这两路,没有第三种被广泛采用的方案。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
牺牲一个单元法(最常用):
- 队空条件:
front == rear - 队满条件:
(rear + 1) % capacity == front - 实际可用容量是
capacity - 1,比如声明int data[10],最多存 9 个元素 - 优点:无需额外成员变量,
sizeof更小,缓存友好 - 缺点:容量设计时得预留 1,容易在初始化时写错
maxsize = 10却以为能存 10 个
记录元素个数法(更直觉):
- 加一个
int size成员,初始为 0 - 队空:
size == 0;队满:size == capacity - 每次
enqueue()后size++,dequeue()后size-- - 优点:容量利用率 100%,判读无歧义,调试时直接看
size就知道剩多少 - 缺点:多一次内存写(
size更新),多占 4 字节,且要注意多线程下非原子操作需加锁
C++ 实现中容易踩的坑
这些不是理论问题,是真实编译/运行时掉进去过的点:
-
rear和front类型别用unsigned int:做(rear + 1) % capacity没问题,但若后续要算长度(如(rear - front + capacity) % capacity),unsigned下rear 会导致极大正数,结果错 - 模运算优先级陷阱:
rear + 1 % capacity≠(rear + 1) % capacity,必须加括号 - 初始化时没把
front和rear都设为 0:有些教程写成front = 0; rear = -1;,这会破坏所有判据,统一用0起始最安全 - 出队不检查空就访问
data[front]:未定义行为,可能读到栈上残留值或触发段错误 - 用
std::array时忘了max_size()是编译期常量,别试图运行时传参改容量
真正难的不是写对一次,而是让 enqueue()/dequeue() 在边界反复调用几十轮后,front、rear、size(如果用了)三者仍自洽——建议写完立刻用容量为 2 或 3 的小数组跑满/空/交替用例,比看十遍原理管用。

















