0

I'm completely new to C and I'm trying to implement a queue data structure. I don't know much about using pointers and storing data, so I'm having difficulty starting. The queue works as a fixed size circular buffer.

I only need a single queue_t.  The queue_t or struct queue contains a long * that holds the data stored in the queue.  It is also important that there are no memory/data leaks in my code.

I was just wondering if I could receive some help on how exactly to start, especially with the queue_new function, but also the other functions like enqueue as well? I'm sure I will need to use functions such as malloc. Here is the code I'm starting out with:

/*
 * Queue implementation.
 *
 */
#include <assert.h>
#include <stdlib.h>

#include "queue.h"

/** The main data structure for the queue. */
struct queue{
  unsigned int back;      /* The next free position in the queue
                           * (i.e. the end or tail of the line) */
  unsigned int front;     /* Current 'head' of the queue
                           * (i.e. the front or head of the line) */
  unsigned int size;      /* How many total elements we currently have enqueued. */
  unsigned int capacity;  /* Maximum number of items the queue can hold */
  long *data;             /* The data our queue holds  */
};

/** 
 * Construct a new empty queue.
 *
 * Returns a pointer to a newly created queue.
 * Return NULL on error
 */

queue_t *queue_new(unsigned int capacity) {


  /* needs finishing */
  queue_t *q = NULL;

  return q;

}

/**
 * Check if the given queue is empty
 *
 * Returns a non-0 value if the queue is empty, 0 otherwise.
 */

int queue_empty(queue_t *q) {

  assert(q != NULL);

  /* needs finishing */

  return 0;

}

/*
 * Check if the given queue is full
 *
 * Returns a non-0 value if the queue is empty, 0 otherwise.
 */

int queue_full(queue_t *q) {

  assert(q != NULL);


  /* needs finishing */

  return 0;

}

/** 
 * Enqueue a new item.
 *
 * Push a new item into our data structure.
 */

void queue_enqueue(queue_t *q, long item) {

  assert(q != NULL);

  assert(q->size < q->capacity);

  /* needs finishing */

}


/** 
 * Queue size.
 *
 * Queries the current size of a queue (valid size must be >= 0).
 */

unsigned int queue_size(queue_t *q) {

  assert(q != NULL);

  /* needs finishing */

  return 0;

}


/**
 * Dequeue an item.
 *
 * Returns the item at the front of the queue and removes an item from the 
 * queue.
 *
 * Note: Removing from an empty queue is an undefined behavior (i.e., it could 
 * crash the program)
 */

long queue_dequeue(queue_t *q) {

  assert(q != NULL);

  assert(q->size > 0);



  /* needs finishing */
  return -1;

}

/** 
 * Delete queue.
 * 
 * Remove the queue and all of its elements from memory.
 *
 * Note: This should be called before the proram terminates.
 */

void queue_delete(queue_t* q) {

  assert(q != NULL);

  /* needs finishing */



}

The code references a queue.h file which acts as an interface, which I will provide here:

#ifndef _QUEUE_H
#define _QUEUE_H

/** Our queue type (fields are hidden in the implementation file). */
typedef struct queue queue_t;

/** 
 * Construct a new empty queue.
 *
 * Returns a pointer to a newly created queue.
 * Return NULL on error
 */
queue_t *queue_new(unsigned int capacity);
/**
 * Check if the given queue is empty
 *
 * Returns a non-0 value if the queue is empty, 0 otherwise.
 */
int queue_empty(queue_t *q);

/**
 * Check if the given queue is full.
 *
 * Returns a non-0 value if the queue is empty, 0 otherwise.
 */
int queue_full(queue_t *q);

/** 
 * Enqueue a new item.
 *
 * Push a new item into our data structure.
 */
void queue_enqueue(queue_t *q, long item);

/**
 * Dequeue an item.
 *
 * Returns the item at the front of the queue and removes an item from the 
 * queue.
 *
 * Note: Removing from an empty queue is an undefined behavior (i.e., it could 
 * crash the program)
 */
long queue_dequeue(queue_t *q);

/** 
 * Queue size.
 *
 * Queries the current size of a queue (valid size must be >= 0).
 */
unsigned int queue_size(queue_t *q);

/** 
 * Delete queue.
 * 
 * Remove the queue and all of its elements from memory.
 *
 * Note: This should be called before the program terminates.
 */
void queue_delete(queue_t *q);


#endif /* ifndef _QUEUE_H */
jamess
  • 1
  • 1
  • Is `data` going to be an array (with contiguous memory)? – Fiddling Bits Oct 13 '22 at 16:43
  • Yes, data is going to be an array. – jamess Oct 13 '22 at 16:44
  • And do you know how to index elements of an array? And how to allocate an array dynamically? (Hint: you might want to re-read the course's notes.) – the busybee Oct 13 '22 at 17:01
  • Don't try to implement all function at once. Start with the first, and implement the first step. Use for example a debugger to check that it works as you expect it. Only then proceed to the next step. – the busybee Oct 13 '22 at 17:02
  • I do not, I'm meaning to use this program as a way to learn how. I do have sources on how to do so, I'm reading those but feeling confused on how to do so in the context of this program. – jamess Oct 13 '22 at 17:04
  • do you have suggestions on what to do inside the first function, queue_new? – jamess Oct 13 '22 at 17:05
  • In `queue_new` you probably have to allocate memory for a `queue_t` and for a data array of the specified capacity, initialize the members of the `queue_t` and return its address, with error handling for all steps. – Bodo Oct 13 '22 at 18:10
  • how much memory should I be allocating? – jamess Oct 13 '22 at 19:31
  • You can find a queue implementation in this answer to [How to print level-order binary search tree with parent node?](https://stackoverflow.com/a/47584201/15168) It uses a fixed-size queue. Some of the differences that you'll need to deal with are: (1) it doesn't explicitly count how many entries are in the queue (it is a derivable value), (2) it doesn't explicitly record how big the queue is, and (3) it doesn't dynamically allocate the queue data. The queued data type is also a `struct bfs_node` instead of a `long`. – Jonathan Leffler Oct 13 '22 at 22:47

1 Answers1

0

Completed my code, here it is. Thanks to everyone who helped me in the comments!

/*
 * Queue implementation.
 *
 * - Implement each of the functions to create a working circular queue.
 * - Do not change any of the structs
 * - When submitting, You should not have any 'printf' statements in your queue 
 *   functions. 
 */
#include <assert.h>
#include <stdlib.h>

#include "queue.h"

/** The main data structure for the queue. */
struct queue{
    unsigned int back;      /* The next free position in the queue
                 * (i.e. the end or tail of the line) */
    unsigned int front;     /* Current 'head' of the queue
                 * (i.e. the front or head of the line) */
    unsigned int size;      /* How many total elements we currently have enqueued. */
    unsigned int capacity;  /* Maximum number of items the queue can hold */
    long *data;             /* The data our queue holds  */
};

/** 
 * Construct a new empty queue.
 *
 * Returns a pointer to a newly created queue.
 * Return NULL on error
 */
queue_t *queue_new(unsigned int capacity) {

    /* [TODO] Complete the function */

    queue_t *q = (queue_t*)malloc(sizeof(queue_t));
    q->front = 0;
    q->back = 0;
    q->size = 0;
    q->capacity = capacity; 
    q->data = (long*)malloc(q->capacity*sizeof(long));
    return q;
}

/**
 * Check if the given queue is empty
 *
 * Returns a non-0 value if the queue is empty, 0 otherwise.
 */
int queue_empty(queue_t *q) {
    assert(q != NULL);
    return (q->size == 0);
}

/**
 * Check if the given queue is full.
 *
 * Returns a non-0 value if the queue is empty, 0 otherwise.
 */
int queue_full(queue_t *q) {
    assert(q != NULL);
    if(queue_size(q) == q->capacity){
        return 1;
    }
    /* [TODO] Complete the function */

    return 0;
}

/** 
 * Enqueue a new item.
 *
 * Push a new item into our data structure.
 */
void queue_enqueue(queue_t *q, long item) {
    assert(q != NULL);
    assert(q->size < q->capacity);

    /* [TODO] Complete the function */
    if(queue_full(q)){
        perror("This Queue is full");
        exit(EXIT_FAILURE);
    }
    else{

        q->data[q->back] = item;
        q->size++;
        q->back = (q->back + 1) % q->capacity;
    }
}

/**
 * Dequeue an item.
 *
 * Returns the item at the front of the queue and removes an item from the 
 * queue.
 *
 * Note: Removing from an empty queue is an undefined behavior (i.e., it could 
 * crash the program)
 */
long queue_dequeue(queue_t *q) {
    assert(q != NULL);
    assert(q->size > 0);

    /* [TODO] Complete the function */
    long res = q->data[q->front];
    if(queue_empty(q)){
        perror("This Queue is empty");
        exit(EXIT_FAILURE);
    }
    else{

        q->front = (q->front + 1) % q->capacity;  // circular queue
        q->size--;
    }
    return res;

}

/** 
 * Queue size.
 *
 * Queries the current size of a queue (valid size must be >= 0).
 */
unsigned int queue_size(queue_t *q) {
    assert(q != NULL);

    /* [TODO] Complete the function */

    return q->size;
}

/** 
 * Delete queue.
 * 
 * Remove the queue and all of its elements from memory.
 *
 * Note: This should be called before the proram terminates.
 */
void queue_delete(queue_t* q) {
    assert(q != NULL);
    free(q->data);
    free(q);
    /* [TODO] Complete the function */

}
jamess
  • 1
  • 1