环缓冲困难

时间:2016-02-04 10:50:35

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

我尝试在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个值,那么它会下溢(或者是环形缓冲区的正常行为吗?)。 我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

循环

for (int32_t i = 1; i < size; ++i)
如果argc = 2

不会循环播放

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

if (queue->front == -1)
由于dequeuetrue函数中的

始终为init_queue

传递更多参数也是如此。由于i=1的起始值,您总是跳过一个参数。

答案 1 :(得分:0)

我认为你的一些索引错了。在您的实施中:

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

当您入队时,您应按以下顺序执行这些步骤:

  • 检查溢出;
  • 当enyuqueing为空队列时,将特殊值-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->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函数是个好主意。