You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于指定结构在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 17:31:06