如何基于指定结构在C语言中实现无内存泄漏的循环缓冲区
C语言固定大小循环缓冲区队列实现指南
先明确接口定义(queue.h)
以下是符合需求的标准队列接口,包含核心操作函数:
#ifndef QUEUE_H #define QUEUE_H typedef struct queue queue_t; // 创建指定容量的队列 queue_t *queue_new(size_t capacity); // 销毁队列,释放所有内存 void queue_destroy(queue_t *queue); // 入队操作:成功返回1,队列满返回0 int queue_enqueue(queue_t *queue, long value); // 出队操作:成功返回1,队列空返回0,结果存入out参数 int queue_dequeue(queue_t *queue, long *out); // 判断队列是否为空 int queue_is_empty(const queue_t *queue); // 判断队列是否已满 int queue_is_full(const queue_t *queue); #endif // QUEUE_H
queue_t结构体定义
按照要求包含long*存储指针,同时补充循环缓冲区必需的控制变量:
struct queue { long *data; // 存储数据的动态数组指针 size_t capacity; // 队列最大容量 size_t head; // 队头索引(下一个要取出的元素位置) size_t tail; // 队尾索引(下一个要插入的元素位置) size_t count; // 当前队列元素数量(简化空满判断) };
用count变量判断空满是新手友好的方案,能避免单纯用head/tail相等时的歧义问题。
核心函数实现详解
1. queue_new(重点实现)
这是队列创建的入口,需严格处理内存分配和失败回滚,避免泄漏:
#include <stdlib.h> #include "queue.h" queue_t *queue_new(size_t capacity) { // 容量为0直接返回NULL if (capacity == 0) { return NULL; } // 第一步:分配queue_t结构体内存 queue_t *queue = malloc(sizeof(queue_t)); if (queue == NULL) { return NULL; } // 第二步:分配存储数据的数组,容量为capacity个long类型 queue->data = malloc(sizeof(long) * capacity); if (queue->data == NULL) { free(queue); // 数组分配失败时,先释放已分配的结构体,避免内存泄漏 return NULL; } // 第三步:初始化控制变量 queue->capacity = capacity; queue->head = 0; queue->tail = 0; queue->count = 0; return queue; }
关键细节:
- 每一次
malloc都要检查返回值,若某一步失败,必须释放之前已分配的内存。 - 初始状态下
head和tail都指向数组起始位置,count为0表示队列为空。
2. queue_destroy(防内存泄漏关键)
必须按顺序释放内存:先释放数据数组,再释放队列结构体:
void queue_destroy(queue_t *queue) { if (queue == NULL) { return; // 空指针直接返回,避免崩溃 } free(queue->data); // 先释放子内存块 free(queue); // 再释放父结构体 }
3. queue_enqueue(入队操作)
int queue_enqueue(queue_t *queue, long value) { if (queue == NULL || queue_is_full(queue)) { return 0; // 空指针或队列已满,返回失败 } // 将值存入队尾位置 queue->data[queue->tail] = value; // 队尾循环前进:到达数组末尾后回到起始位置 queue->tail = (queue->tail + 1) % queue->capacity; queue->count++; // 元素计数加1 return 1; }
4. queue_dequeue(出队操作)
int queue_dequeue(queue_t *queue, long *out) { if (queue == NULL || out == NULL || queue_is_empty(queue)) { return 0; // 参数非法或队列为空,返回失败 } // 取出队头元素 *out = queue->data[queue->head]; // 队头循环前进 queue->head = (queue->head + 1) % queue->capacity; queue->count--; // 元素计数减1 return 1; }
5. 辅助判断函数
int queue_is_empty(const queue_t *queue) { return (queue != NULL) && (queue->count == 0); } int queue_is_full(const queue_t *queue) { return (queue != NULL) && (queue->count == queue->capacity); }
新手必注意项
- 空指针检查:所有函数都要先判断输入指针是否为
NULL,避免空指针解引用崩溃。 - 内存释放顺序:必须先释放子内存块(数据数组),再释放父结构体,否则会造成泄漏。
- 循环索引计算:用
% queue->capacity实现循环,避免数组越界。 - 空满判断逻辑:用
count变量比单纯比较head和tail更直观,不会出现歧义。
内容的提问来源于stack exchange,提问作者jamess
相关产品推荐
相关产品推荐

