C 排队/退队奇怪?

C 排队/退队奇怪?,c,queue,free,C,Queue,Free,我一直在做一个任务,它涉及到实现包含void指针的队列,这样就可以对任何类型的数据进行泛化。我目前遇到了一个奇怪的问题,即退出队列的节点减少了列表的大小,但没有返回我期望的节点。在出列操作中省略对free()的调用可以纠正这一点,但由于我想释放出列节点,这是不可取的。有什么建议吗 测试运行:例程.c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "queue.h" i

我一直在做一个任务,它涉及到实现包含void指针的队列,这样就可以对任何类型的数据进行泛化。我目前遇到了一个奇怪的问题,即退出队列的节点减少了列表的大小,但没有返回我期望的节点。在出列操作中省略对free()的调用可以纠正这一点,但由于我想释放出列节点,这是不可取的。有什么建议吗

测试运行:例程.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”
int main(){
队列测试=生成队列();
排队(“一”,测试);
排队(“两个”,测试);
printf(“项目为%s!\n”,(字符*)出列(测试));
printf(“项目为%s!\n”,(字符*)出列(测试));
返回0;
}
队列.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;
}
#包括
#包括
#包括
/*队列被实现为指向此处未指定的结构的指针*/
typedef struct queue_structure*队列;
结构节点{
结构节点*下一步;
作废*数据;
};
结构队列结构{
结构节点*头部;
结构节点*尾部;
};
/*功能协议列表*/
bool是一个空队列(队列q);
/*make_queue函数返回一个新创建的没有值的队列
储存在里面。
*/
队列生成队列(){
queue newQueue=malloc(sizeof(struct queue_structure));
返回新队列;
}
/*enqueue函数向队列添加一个值。虽然这个功能
不更改指针q,q指向的结构的字段
在调用此函数的过程中,可以修改点。
*/
无效排队(无效*值,队列q){
结构节点*新节点=(结构节点*)malloc(sizeof(结构节点));
新建节点->数据=值;
如果(是否为空队列(q))
q->tail=newNode;
新建节点->下一步=q->头部;
q->head=newNode;
}
/*dequeue函数从队列中删除一个值并返回它。
虽然此函数不会更改指针q,但
在调用过程中可以修改q点的结构
这个功能。
此函数的前提条件是至少存储一个值
在队列中。
*/
作废*出列(队列q){
如果(!q->head->next){//队列中只有一个项目。
printf(“队列中只有一项!\n”);
结构节点*to_dequeue=q->tail;
void*data=q->head->data;
免费(去排队);
q->head=NULL;
q->tail=NULL;
返回数据;
}
else{//队列中有多个项目。
printf(“队列中的多个项目!\n”);
结构节点*to_dequeue=q->tail;
void*data=q->tail->data;
结构节点*trace=q->head;
while(trace->next&&trace->next!=q->tail)
跟踪=跟踪->下一步;
免费(去排队);
q->tail=trace;
q->tail->next=NULL;
返回数据;
}
}
/*函数的front_of_queue返回队列前面的值
(即,最近最不添加到队列中的一个)而不删除
从队列中删除该值。它没有副作用。
此函数的前提条件是至少存储一个值
在队列中。
*/
void*队列前面的队列(队列q){
返回q->head->数据;
}
/*is_empty_queue函数确定队列是否为空,
如果队列中未存储任何值,则返回真布尔值
如果一个或多个值存储在
队列
*/
bool为空队列(队列q){
如果(q->head)
返回1;
返回0;
}

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

bool is_empty_queue(queue q) {
  if(q->head)
    return 1;
  return 0;
}
这使得
排队
行为异常

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,可能
head
tail
最初为
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)
所有进一步的
排队
操作将离开
head==tail
。但是如果您现在退出队列:

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
头是一个悬挂的指针

案例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)->next!=0
,则如果您退出队列,
trace
将设置为野生“指针”
x
,然后选中
x->next
,但由于
x
是不确定的位模式,因此通常会导致segfault

除非我忽略了什么,否则在构建时初始化
头部
尾部
,修复
是空的_队列
,并检查
出列
上的空性将为您提供一个工作程序


但是,如果队列很长,则出队列操作会很慢,因为它必须遍历整个队列以找到倒数第二个要更新的元素
tail
。如果您在
tail
位置排队,并且从
head
dequeue
并且在
make\queue
中没有将
head
tail
初始化为
NULL
,并且您的空性测试错误,则可以同时执行,
enqueue
dequeue
,O(1)操作

bool is_empty_queue(queue q) {
  if(q->head)
    return 1;
  return 0;
}
这使得
排队
行为异常

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,可能
head
tail
最初为
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)
所有进一步的
排队
操作将离开
head==tail
。但是如果您现在退出队列:

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
头是一个悬挂的指针

案例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)->next!=0
,则如果您退出队列,
trace
将设置为野生“指针”
x
,然后选中
x->next
,但由于
x
是不确定的位模式,因此通常会导致segfault

除非我忽略了什么,否则在构建时初始化
头部
尾部
,修复
是空的_队列
,并检查
出列
上的空性将为您提供一个工作程序

但是如果队列很长,则出列操作很慢