【问题标题】:Queue/dequeue oddity?队列/出队奇怪?
【发布时间】:2012-01-27 05:26:30
【问题描述】:

我一直在从事一项涉及实现包含 void 指针的队列的任务,以便可以将它们推广到任何类型的数据。我目前遇到了一个奇怪的问题,虽然出队节点会减少列表的大小,但不会返回我期望的节点。在出队操作中省略对 free() 的调用可以解决此问题,但是由于我想释放出队的节点,因此这是不可取的。有什么建议吗?

测试运行:routine.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "queue.h"

int main() {
  queue test = make_queue();
  enqueue("One", test);
  enqueue("Two", test);
  printf("Item is %s!\n", (char *)dequeue(test));
  printf("Item is %s!\n", (char *)dequeue(test));
  return 0;
}

queue.h

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
/* A queue is implemented as a pointer to a structure not specified here. */

typedef struct queue_structure *queue;

struct node {
  struct node * next;
  void * data;
};

struct queue_structure {
  struct node * head;
  struct node * tail;
};

/* List of function protocols. */
bool is_empty_queue(queue q);

/* The make_queue function returns a newly created queue with no values
   stored in it.
*/

queue make_queue() {
  queue newQueue = malloc(sizeof(struct queue_structure));
  return newQueue;
}

/* The enqueue function adds a value to a queue.  Although this function
   does not change the pointer q, fields of the structure to which q
   points may be modified in the course of a call to this function.
*/

void enqueue(void *value, queue q) {
  struct node * newNode = (struct node *)malloc(sizeof(struct node));
  newNode->data = value;    
  if(is_empty_queue(q))
    q->tail = newNode;
  newNode->next = q->head;
  q->head = newNode;
}

/* The dequeue function removes a value from a queue and returns it.
   Although this function does not change the pointer q, fields of the
   structure to which q points may be modified in the course of a call to
   this function.

   It is a precondition of this function that at least one value is stored
   in the queue.
*/

void *dequeue(queue q) {
  if(!q->head->next) { // Only a single item in the queue.
    printf("Only one item in queue!\n");
    struct node * to_dequeue = q->tail;
    void * data = q->head->data;
    free(to_dequeue);
    q->head = NULL;
    q->tail = NULL;
    return data;
  }
  else { // Multiple items in the queue.
    printf("Several items in queue!\n");
    struct node * to_dequeue = q->tail;
    void * data = q->tail->data;
    struct node * trace = q->head;
    while(trace->next && trace->next != q->tail)
      trace = trace->next;
    free(to_dequeue);
    q->tail = trace;
    q->tail->next = NULL;
    return data;
  }
}

/* The front_of_queue function returns the value at the front of a queue
   (that is, the one least recently added to the queue) without removing
   that value from the queue.  It has no side effect.

   It is a precondition of this function that at least one value is stored
   in the queue.
*/

void *front_of_queue(queue q) {
  return q->head->data;
}

/* The is_empty_queue function determines whether a queue is empty,
   returning the true Boolean value if no values are stored in the queue
   and the false Boolean value if one or more values are stored in the
   queue.
*/

bool is_empty_queue(queue q) {
  if(q->head)
    return 1;
  return 0;
}

【问题讨论】:

  • 一个小提示:您通过将所有内容都放在一个头文件中来混合接口和实现。这意味着你不能让两个不同的源文件使用队列,因为你会得到链接器错误。将实现(函数中的实际代码)放在单独的源文件中,头文件中只有结构和函数原型。
  • 另一个注意事项:您期望函数的参数是有效的,但它们可能不是。此外,例如在dequeue 中,您必须检查是否有空队列。对于一个简单的家庭作业应用程序来说可能没问题,但是好习惯(比如检查论点和类似的东西)最好早点学习。 :)

标签: c queue free


【解决方案1】:

您没有在make_queue 中将headtail 初始化为NULL,并且您的空性测试错误,

bool is_empty_queue(queue q) {
  if(q->head)
    return 1;
  return 0;
}

这使得enqueue 行为异常。

void enqueue(void *value, queue q) {
  struct node * newNode = (struct node *)malloc(sizeof(struct node));
  newNode->data = value;    
  if(is_empty_queue(q))
    q->tail = newNode;
  newNode->next = q->head;
  q->head = newNode;
}

案例 1,可能 headtail 最初是 NULL

head -> 0; tail -> 0  // now enqueue 1
is_empty_queue(q) returns 0 since q->head == NULL, so q->tail still points to 0
n(1)->next = 0
head = n(1)

results in
head -> n(1) -> 0; tail -> 0  // next enqueue 2
is_empty_queue(q) returns 1 since q->head = n(1) != 0, so
q->tail = n(2)
n(2)->next = n(1)
q->head = n(2)

result:
head -> n(2) -> n(1) -> 0; tail -> n(2)

所有进一步的enqueue 操作将离开head == tail。但是如果你现在dequeue:

struct node * to_dequeue = q->tail;   // n(2)
void * data = q->tail->data;
struct node * trace = q->head;        // n(2)
while(trace->next && trace->next != q->tail)  // n(2) -> n(1) -> 0
  trace = trace->next;                // trace = n(1)
free(to_dequeue);                     // free n(2)
q->tail = trace;                      // tail -> n(1)
q->tail->next = NULL;                 // already had that

head 是一个悬空指针。

案例 2,可能 head 最初不是 NULL

head -> x; tail -> y // enqueue 1
is_empty_queue(q) returns 1 because q->head == x != 0
q->tail = n(1)
n(1)->next = x
q->head = n(1)

head -> n(1) -> x; tail -> n(1) // now enqueue 2
is_empty_queue(q) returns 1 because q->head == n(1)
q->tail = n(2)
n(2)->next = n(1)
q->head = n(2)

head -> n(2) -> n(1) -> x; tail -> n(2)

唯一的区别是现在n(1)-&gt;next != 0,然后如果你出队,trace 将被设置为野'指针'x 然后检查x-&gt;next,但因为x 是一个不确定的位模式,这通常会导致段错误。

除非我忽略了某些事情,否则在构造时初始化 headtail,修复 is_empty_queue 并检查 dequeue 上的空虚将为您提供一个工作程序。

但是如果队列很长,出队操作会很慢,因为它必须遍历整个队列才能找到倒数第二个元素来更新tail。你可以同时拥有enqueuedequeue,如果你在tail 位置和dequeuehead 入队,O(1) 操作。

【讨论】:

  • 我认为:(emptQueue) if(element.first == NULL){ return TRUE;否则 ... 是最好和最快的 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 2020-11-14
  • 2015-08-23
  • 2017-06-27
相关资源
最近更新 更多