队列接口
Queue interface
我必须为一个更大的项目实现一个 FIFO 队列接口。
队列需要处理任何类型的数据,因此我决定使用 void*
指针作为队列节点的值,并使用双链表。每个节点都有一个value
、一个prev
和一个next
.
为了更好理解,请注意front
指的是队列中的第一个节点,back
是最后加入的节点。
我写了一个enqueue
和一个dequeue
函数,还有一个peek_back
和一个peek_front function
。
但是,当我 运行 在我的 main()
函数中进行一些测试时,看起来有些问题:
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
int main(){
queue_t* q = init_queue();
if(q == NULL){
printf("Error: q not initialized properly\n");
return 1;
}
//Test with a struct
struct point {
int x;
int y;
};
for(int i = 1; i <= 3; i++){
struct point p = {i,i};
struct point* p_ptr = &p;
enqueue(q, (void*) p_ptr);
printf("One node added, front value: %i, back value: %i\n", ((struct point*)peek_front(q))->x, ((struct point*)peek_back(q))->x);
}
int result;
int size;
while(q->size > 0){
result = *(int*) dequeue(q);
size = q->size;
printf("One node removed, value = %i. Size is now %i \n", result, size);
}
return 0;
}
输出为:
gcc main.c queue.c -o main.o -Wall -Werror
./main.o
One node added, front value: 1, back value: 1
One node added, front value: 2, back value: 2
One node added, front value: 3, back value: 3
One node removed, value = 3. Size is now 2
One node removed, value = 3. Size is now 1
One node removed, value = 3. Size is now 0
如您所见,enqueue
更改了队列的前端节点,尽管它不应该,并且 dequeue
总是 return 相同的值...
您知道问题出在哪里吗?我尝试使用 gdb 进行调试,但我真的很难发现问题所在。另外,我很高兴听到您对此队列的一般实现的反馈...欢迎任何建议,因为我只是 C 语言的初学者 ;)
这是我的 queue.h
头文件:
#ifndef QUEUE_H
#define QUEUE_H
typedef struct node{
struct node* next;
struct node* prev;
void* value;
} node_t;
typedef struct queue{
struct node* front;
struct node* back;
int size;
} queue_t;
queue_t* init_queue();
int enqueue(queue_t* q, void* val);
void* dequeue(queue_t* q);
int get_size(queue_t* q);
int is_empty(queue_t * q);
void* peek_front(queue_t * q);
void* peek_back(queue_t * q);
#endif
这是我的 queue.c
文件:
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
/*
* Allocate memory and initialize a new queue with size = 0 and front = NULL
* @return: initialized queue if succes, NULL if failed malloc
*/
queue_t* init_queue(){
queue_t* newQueue = (queue_t*)malloc(sizeof(queue_t));
if(!newQueue){
printf("Malloc error in init_queue\n");
return NULL;
}
newQueue->front = NULL;
newQueue->back = NULL;
newQueue->size = 0;
return newQueue;
}
/*
* Add a new node with value @val at the end (back) of the queue @q
* @q: a valid queue (queue_t type)
* @val: the value to be added
* return 0 if success, -1 if failure
*/
int enqueue(queue_t* q, void* val){
struct node *newNode = (struct node*) malloc(sizeof(struct node));
if(!newNode) return -1;
newNode->value = val;
if(q->size == 0){
newNode->next = newNode;
newNode->prev = newNode;
q->front = newNode;
q->back = newNode;
}
else{
newNode->next = q->back;
newNode->prev = q->front;
q->back->prev = newNode;
q->front->next = newNode;
q->back = newNode;
}
q->size ++;
return 0;
}
/*
* Remove front node of queue @q and return its value
* @q: a valid queue
* @return: value of removed front node (int) if success
* NULL if empty queue
*/
void* dequeue(queue_t* q){
if(q->size == 0){
printf("Warning: empty queue\n");
return NULL;
}
void* tbr = q->front->value; //to be returned
if(q->size == 1){
free(q->front);
q->front = NULL;
}else{
struct node * old_front = q->front;
old_front->prev->next = q->back;
q->back->prev = old_front->prev;
q->front = old_front->prev;
}
q->size--;
return tbr;
}
/*
* @q: a valid queue
* @return: size of queue @q if q is not NULL
* -1 if q is NULL
*/
int get_size(queue_t* q){
if(q != NULL){
return q->size;
}else{
return -1;
}
}
/*
* Check wether @q is empty or not
* @q: a valid queue
* @return: 1 if @q is empty, 0 otherwise
*/
int is_empty(queue_t * q){
return get_size(q) == 0;
}
/*
* Peek the value of front node of queue @q, but doesn't remove it
* @q: a valid queue
* @return: value of front node
*/
void* peek_front(queue_t * q){
if (q->size == 0){
printf("Peeking an empty queue\n");
return NULL;
}else{
return q->front->value;
}
}
/*
* Peek the value of last node (last added, back of the tail)
* from queue @q, but doesn't remove it
* @q: a valid queue
* @return: value of last-added node (back)
*/
void* peek_back(queue_t * q){
if (q->size == 0){
printf("Peeking an empty queue\n");
return NULL;
}else{
return q->back->value;
}
}
在 main()
中,您这样做:
for(int i = 1; i <= 3; i++){
struct point p = {i,i}; // HERE : automatic local inside loop
struct point* p_ptr = &p; // HERE : acquire the address of that local var
enqueue(q, (void*) p_ptr); // HERE : store said address in the queue
printf("One node added, front value: %i, back value: %i\n", ((struct point*)peek_front(q))->x, ((struct point*)peek_back(q))->x);
} // HERE : address stored is no longer valid.
动态分配是一种解决方案,而且相当简单。 Sans 错误检查是这样的:
for (int i = 1; i <= 3; i++)
{
struct point *p = malloc(sizeof *p);
p->x = p->y = i;
enqueue(q, p);
printf("One node added, front value: %i, back value: %i\n",
((struct point*)peek_front(q))->x, ((struct point*)peek_back(q))->x);
}
while (q->size > 0) {
struct point *p = dequeue(q);
printf("One node removed, value = (%i,%i). Size is now %i \n", p->x, p->y, q->size);
free(p); // NOTE, freeing the dynamic memory we allocated during insertion.
}
free(q);
输出
One node added, front value: 1, back value: 1
One node added, front value: 1, back value: 2
One node added, front value: 1, back value: 3
One node removed, value = (1,1). Size is now 2
One node removed, value = (2,2). Size is now 1
One node removed, value = (3,3). Size is now 0
我必须为一个更大的项目实现一个 FIFO 队列接口。
队列需要处理任何类型的数据,因此我决定使用 void*
指针作为队列节点的值,并使用双链表。每个节点都有一个value
、一个prev
和一个next
.
为了更好理解,请注意front
指的是队列中的第一个节点,back
是最后加入的节点。
我写了一个enqueue
和一个dequeue
函数,还有一个peek_back
和一个peek_front function
。
但是,当我 运行 在我的 main()
函数中进行一些测试时,看起来有些问题:
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
int main(){
queue_t* q = init_queue();
if(q == NULL){
printf("Error: q not initialized properly\n");
return 1;
}
//Test with a struct
struct point {
int x;
int y;
};
for(int i = 1; i <= 3; i++){
struct point p = {i,i};
struct point* p_ptr = &p;
enqueue(q, (void*) p_ptr);
printf("One node added, front value: %i, back value: %i\n", ((struct point*)peek_front(q))->x, ((struct point*)peek_back(q))->x);
}
int result;
int size;
while(q->size > 0){
result = *(int*) dequeue(q);
size = q->size;
printf("One node removed, value = %i. Size is now %i \n", result, size);
}
return 0;
}
输出为:
gcc main.c queue.c -o main.o -Wall -Werror
./main.o
One node added, front value: 1, back value: 1
One node added, front value: 2, back value: 2
One node added, front value: 3, back value: 3
One node removed, value = 3. Size is now 2
One node removed, value = 3. Size is now 1
One node removed, value = 3. Size is now 0
如您所见,enqueue
更改了队列的前端节点,尽管它不应该,并且 dequeue
总是 return 相同的值...
您知道问题出在哪里吗?我尝试使用 gdb 进行调试,但我真的很难发现问题所在。另外,我很高兴听到您对此队列的一般实现的反馈...欢迎任何建议,因为我只是 C 语言的初学者 ;)
这是我的 queue.h
头文件:
#ifndef QUEUE_H
#define QUEUE_H
typedef struct node{
struct node* next;
struct node* prev;
void* value;
} node_t;
typedef struct queue{
struct node* front;
struct node* back;
int size;
} queue_t;
queue_t* init_queue();
int enqueue(queue_t* q, void* val);
void* dequeue(queue_t* q);
int get_size(queue_t* q);
int is_empty(queue_t * q);
void* peek_front(queue_t * q);
void* peek_back(queue_t * q);
#endif
这是我的 queue.c
文件:
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
/*
* Allocate memory and initialize a new queue with size = 0 and front = NULL
* @return: initialized queue if succes, NULL if failed malloc
*/
queue_t* init_queue(){
queue_t* newQueue = (queue_t*)malloc(sizeof(queue_t));
if(!newQueue){
printf("Malloc error in init_queue\n");
return NULL;
}
newQueue->front = NULL;
newQueue->back = NULL;
newQueue->size = 0;
return newQueue;
}
/*
* Add a new node with value @val at the end (back) of the queue @q
* @q: a valid queue (queue_t type)
* @val: the value to be added
* return 0 if success, -1 if failure
*/
int enqueue(queue_t* q, void* val){
struct node *newNode = (struct node*) malloc(sizeof(struct node));
if(!newNode) return -1;
newNode->value = val;
if(q->size == 0){
newNode->next = newNode;
newNode->prev = newNode;
q->front = newNode;
q->back = newNode;
}
else{
newNode->next = q->back;
newNode->prev = q->front;
q->back->prev = newNode;
q->front->next = newNode;
q->back = newNode;
}
q->size ++;
return 0;
}
/*
* Remove front node of queue @q and return its value
* @q: a valid queue
* @return: value of removed front node (int) if success
* NULL if empty queue
*/
void* dequeue(queue_t* q){
if(q->size == 0){
printf("Warning: empty queue\n");
return NULL;
}
void* tbr = q->front->value; //to be returned
if(q->size == 1){
free(q->front);
q->front = NULL;
}else{
struct node * old_front = q->front;
old_front->prev->next = q->back;
q->back->prev = old_front->prev;
q->front = old_front->prev;
}
q->size--;
return tbr;
}
/*
* @q: a valid queue
* @return: size of queue @q if q is not NULL
* -1 if q is NULL
*/
int get_size(queue_t* q){
if(q != NULL){
return q->size;
}else{
return -1;
}
}
/*
* Check wether @q is empty or not
* @q: a valid queue
* @return: 1 if @q is empty, 0 otherwise
*/
int is_empty(queue_t * q){
return get_size(q) == 0;
}
/*
* Peek the value of front node of queue @q, but doesn't remove it
* @q: a valid queue
* @return: value of front node
*/
void* peek_front(queue_t * q){
if (q->size == 0){
printf("Peeking an empty queue\n");
return NULL;
}else{
return q->front->value;
}
}
/*
* Peek the value of last node (last added, back of the tail)
* from queue @q, but doesn't remove it
* @q: a valid queue
* @return: value of last-added node (back)
*/
void* peek_back(queue_t * q){
if (q->size == 0){
printf("Peeking an empty queue\n");
return NULL;
}else{
return q->back->value;
}
}
在 main()
中,您这样做:
for(int i = 1; i <= 3; i++){
struct point p = {i,i}; // HERE : automatic local inside loop
struct point* p_ptr = &p; // HERE : acquire the address of that local var
enqueue(q, (void*) p_ptr); // HERE : store said address in the queue
printf("One node added, front value: %i, back value: %i\n", ((struct point*)peek_front(q))->x, ((struct point*)peek_back(q))->x);
} // HERE : address stored is no longer valid.
动态分配是一种解决方案,而且相当简单。 Sans 错误检查是这样的:
for (int i = 1; i <= 3; i++)
{
struct point *p = malloc(sizeof *p);
p->x = p->y = i;
enqueue(q, p);
printf("One node added, front value: %i, back value: %i\n",
((struct point*)peek_front(q))->x, ((struct point*)peek_back(q))->x);
}
while (q->size > 0) {
struct point *p = dequeue(q);
printf("One node removed, value = (%i,%i). Size is now %i \n", p->x, p->y, q->size);
free(p); // NOTE, freeing the dynamic memory we allocated during insertion.
}
free(q);
输出
One node added, front value: 1, back value: 1
One node added, front value: 1, back value: 2
One node added, front value: 1, back value: 3
One node removed, value = (1,1). Size is now 2
One node removed, value = (2,2). Size is now 1
One node removed, value = (3,3). Size is now 0