【问题标题】:Ring buffer difficulties环形缓冲区困难
【发布时间】:2016-02-04 10:50:35
【问题描述】:

我尝试在 C 中实现环形缓冲区/循环队列。

它应该通过 argv 获取所有参数,将它们一个一个地推入队列,然后以相同的方式将它们从队列中弹出,并在输出时打印出来。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>

struct buffer
{
    int32_t front, rear, capacity, *array;
};

__attribute__ ((noreturn)) void prog_error(const char* str)
{
    perror(str);
    exit(1);
}

struct buffer *init_queue(int32_t size)
{
    struct buffer *queue = malloc(sizeof(struct buffer));

    if (!queue)
        return NULL;

    queue->capacity = size;
    queue->front = -1;
    queue->rear = -1;
    queue->array = malloc(queue->capacity * sizeof(int32_t));

    if (!queue->array)
        return NULL;

    return queue;
}


void enqueue(struct buffer *queue, int32_t x)
{
    if (((queue->rear + 1) % queue->capacity == queue->rear))
        prog_error("Queue overflow");

    queue->rear = (queue->rear + 1) % queue->capacity;
    queue->array[queue->rear] = x;

    if (queue->front == -1)
        queue->front = queue->rear;
}

int32_t dequeue(struct buffer *queue)
{
    int32_t data = 0;

    if (queue->front == -1)
        prog_error("Queue underflow");

    data = queue->array[queue->front];

    if (queue->front == queue->rear)
        queue->front = queue->rear = -1;

    queue->front = (queue->front + 1) % queue->capacity;

    return data;
}

int main(int argc, char **argv)
{
    if (argc < 2)
        prog_error("Too few arguments");

    int32_t size = (int32_t) argc - 1;

    struct buffer *queue;

    if (!(queue = init_queue(size)))
        prog_error("Allocation error");

    for (int32_t i = 1; i < size; ++i)
        enqueue(queue, (int32_t) atoi(argv[i]));

    for (int32_t i = 0; i < size; ++i)
        printf("%" PRId32 "\n", dequeue(queue));

    free(queue);
}

但最后一个值总是用 1 代替。

而且,如果我给它正好 1 个值,那么它就会下溢(或者这是环形缓冲区的正常行为?)。 我该如何解决这个问题?

【问题讨论】:

  • 您的输入、实际输出和预期输出是什么?
  • 输入:./ringbuffer 0 1 2 3 4 期望输出:0 1 2 3 4 实际输出:0 1 2 3 1

标签: c linux data-structures queue circular-buffer


【解决方案1】:

循环

for (int32_t i = 1; i < size; ++i)

argc = 2 不循环

然后,如果您将单个 arg 传递给您的应用程序,则不会在您的队列中插入任何数据,并且

if (queue->front == -1)

由于init_queue,在dequeue 函数中始终为true

同样的事情传递了更多的参数。由于i=1 的起始值,您总是会跳过参数。

【讨论】:

    【解决方案2】:

    我认为您的某些索引有误。在您的实施中:

    • 前面描述了应该出列的下一项;
    • 后面描述了下一项入队的索引;
    • 当前后端都具有特殊值-1时,队列为空;这是区分空队列和满队列所必需的。

    入队时,应按以下顺序执行步骤:

    • 检查溢出;
    • 在进入空队列时将特殊值 -1 调整为合理值;
    • 将项目放置在位置rear;
    • 增加rear,可能会环绕。

    您首先增加后部,然后存储值,然后调整前部。检查溢出时也会出现一次性错误。以下是更正版本:

    void enqueue(struct buffer *queue, int32_t x)
    {
        if (queue->rear >= 0 && (queue->rear) % queue->capacity == queue->front)
            prog_error("Queue overflow");
    
        if (queue->front == -1)
            queue->front = queue->rear = 0;
    
        queue->array[queue->rear] = x;
        queue->rear = (queue->rear + 1) % queue->capacity;
    }
    

    类似的出队:

    • 检查下溢;
    • 将当前最前面的数据保存为结果;
    • 增加前面,注意环绕;
    • 当前部与后部相遇时,将前部和后部调整为特殊值 -1。

    你把最后两点搞混了。 (队列只能在删除一个项目之后为空。)所以:

    int32_t dequeue(struct buffer *queue)
    {
        int32_t data = 0;
    
        if (queue->front == -1)
            prog_error("Queue underflow");
    
        data = queue->array[queue->front];
        queue->front = (queue->front + 1) % queue->capacity;
    
        if (queue->front == queue->rear)
            queue->front = queue->rear = -1;
    
        return data;
    }
    

    您对队列的使用很尴尬。通常,东西在某处排队并在其他地方出队。出队的代码通常不知道队列中有多少项目。因此,队列有一种方法可以判断它是否为空。将空队列出队会导致下溢。

    您可以直接检查(queue-&gt;front,但这里有一个包装函数:

    int isempty(struct buffer *queue)
    {
        return (queue->front < 0);
    }
    

    这导致客户端代码如下:

    int main(int argc, char **argv)
    {
        if (argc < 2)
            prog_error("Too few arguments");
    
        int32_t size = (int32_t) argc - 1;
        struct buffer *queue = init_queue(size);
    
        if (queue == NULL)
            prog_error("Allocation error");
    
        for (int32_t i = 0; i < size; ++i)
            enqueue(queue, (int32_t) atoi(argv[i + 1]));
    
        while (!isempty(queue))
            printf("%" PRId32 "\n", dequeue(queue));
    
        free(queue);
    }
    

    最后,他与 -1 的业务导致一些代码混乱。也许队列更好地表示为frontlength

    struct buffer
    {
        int32_t front;
        int32_t length;
        int32_t capacity;
        int32_t *array;
    };
    
    struct buffer *init_queue(int32_t size)
    {
        struct buffer *queue = malloc(sizeof(struct buffer));
    
        if (!queue) return NULL;
    
        queue->capacity = size;
        queue->front = 0;
        queue->length = 0;
        queue->array = malloc(queue->capacity * sizeof(*queue->array));
    
        if (!queue->array) return NULL;
    
        return queue;
    }
    
    int isempty(struct buffer *queue)
    {
        return (queue->length == 0);
    }
    
    void enqueue(struct buffer *queue, int32_t x)
    {
        if (queue->length == queue->capacity) prog_error("Queue overflow");
    
        queue->array[queue->front + queue->length++] = x;
    }
    
    int32_t dequeue(struct buffer *queue)
    {
        int32_t data = 0;
    
        if (queue->length == 0) prog_error("Queue underflow");
    
        data = queue->array[queue->front++];
        if (queue->front > queue->capacity) queue->front = 0;
        queue->length--;
    
        return data;
    }
    

    然后,我将停止:),您不仅应该为队列结构本身释放内存,还应该为array 成员释放内存。为此创建一个queue_destroy 函数是个好主意。

    【讨论】:

      猜你喜欢
      • 2014-06-14
      • 2012-04-04
      • 1970-01-01
      • 2018-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多