3
\$\begingroup\$

I wrote this header for generic use of a queue. The one thing I'm wondering is if I understood the usage of void*. I hope that if somebody teach me some conventions when coding in C.

/*
* array-based queue implementation by using void*
* written by kidkkr
* May 6 '16
*/
#ifndef QUEUE_H
#define QUEUE_H
#define QUEUE_CAPACITY 10
#include <stdio.h>
typedef struct {
 void* data[QUEUE_CAPACITY];
 int head;
 int tail;
 int size;
} Queue;
void initQueue(Queue* pQueue)
{
 pQueue->head = 0;
 pQueue->tail = -1;
 pQueue->size = 0;
}
void enqueue(Queue* pQueue, void* item)
{
 if (pQueue->size == QUEUE_CAPACITY) // when queue is full
 {
 printf("Queue is full\n");
 return;
 }
 else
 {
 (pQueue->tail)++;
 (pQueue->tail) %= QUEUE_CAPACITY;
 (pQueue->data)[pQueue->tail] = item;
 (pQueue->size)++;
 }
}
void* dequeue(Queue* pQueue)
{
 // Return NULL when queue is empty
 // Return (void*)item at the head otherwise.
 void* item = NULL;
 if (isEmpty(&pQueue))
 {
 printf("Queue is empty\n");
 }
 else
 {
 item = (pQueue->data)[pQueue->head];
 (pQueue->head)++;
 (pQueue->head) %= QUEUE_CAPACITY;
 (pQueue->size)--;
 }
 return item;
}
int isEmpty(Queue* pQueue)
{
 return pQueue->size == 0;
}
#endif
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked May 12, 2016 at 15:58
\$\endgroup\$
2
  • 2
    \$\begingroup\$ Welcome to Code Review! Did you test whether it works as intended? \$\endgroup\$ Commented May 12, 2016 at 17:06
  • \$\begingroup\$ Im sorry. I didn't try compile it... next time I would. \$\endgroup\$ Commented May 13, 2016 at 0:59

3 Answers 3

1
\$\begingroup\$

Your code looks nice and nifty. However, I have a couple of suggestions.

1 I would change the type of head, tail and size from int to size_t.

2

void initQueue(Queue* pQueue)
{
 pQueue->head = 0;
 pQueue->tail = -1;
 pQueue->size = 0;
}

The semantics is that you first update the value of tail and then use it as an index at which you enqueue a new data item. If you specify that you first insert at tail and only after that update it, you effectively get rid of negative value range, i.e., size_t will do just fine.

3 In all functions operating on the queue you should have a sanity check that the input queue pointer is not NULL.

4

void enqueue(Queue* pQueue, void* item)
{
 if (pQueue->size == QUEUE_CAPACITY) // when queue is full
 {
 printf("Queue is full\n");
 return;
 }
 else
 ...

I would #include <stdbool.h> and return true if the enqueuing for successful and false otherwise. Also, it is not funky to print to standard output in a data structure function/algorithm.

5 For debugging purposes you could roll a separate function that neatly prints the contents of your queue.

Summa summarum

All in all, I had this in mind:

queue.h

#ifndef QUEUE_H
#define QUEUE_H
#include <stdbool.h>
#include <stdio.h>
#define QUEUE_CAPACITY 10
typedef struct {
 void* data[QUEUE_CAPACITY];
 size_t head;
 size_t tail;
 size_t size;
} Queue;
bool initQueue(Queue* pQueue)
{
 if (!pQueue)
 {
 return false;
 }
 pQueue->head = 0;
 pQueue->tail = 0;
 pQueue->size = 0;
 return true;
}
int isEmpty(Queue* pQueue)
{
 return pQueue && pQueue->size == 0;
}
bool enqueue(Queue* pQueue, void* item)
{
 if (!pQueue || pQueue->size == QUEUE_CAPACITY) // when queue is full
 {
 return false;
 }
 pQueue->data[pQueue->tail] = item;
 pQueue->tail = (pQueue->tail + 1) % QUEUE_CAPACITY;
 pQueue->size++;
 return true;
}
void* dequeue(Queue* pQueue)
{
 // Return NULL when queue is empty
 // Return (void*)item at the head otherwise.
 void* item;
 if (!pQueue || isEmpty(pQueue))
 {
 return NULL;
 }
 item = pQueue->data[pQueue->head];
 pQueue->head = (pQueue->head + 1) % QUEUE_CAPACITY;
 pQueue->size--;
 return item;
}
void debugPrint(Queue* pQueue)
{
 size_t index;
 size_t tmp;
 if (!pQueue)
 {
 printf("null");
 return;
 }
 printf("[");
 if (pQueue->size >= 1)
 {
 printf("%d", (int) pQueue->data[pQueue->head]);
 }
 for (index = 1; index < pQueue->size; ++index)
 {
 tmp = (pQueue->head + index) % QUEUE_CAPACITY;
 printf(", %d", (int) pQueue->data[tmp]);
 }
 printf("]");
}
#endif

main.c

#include "queue.h"
int main(int argc, const char * argv[]) {
 int i;
 Queue q;
 initQueue(&q);
 for (i = 0; i < QUEUE_CAPACITY; ++i)
 {
 debugPrint(&q);
 puts("");
 enqueue(&q, (void*) i);
 }
 for (i = QUEUE_CAPACITY; i < 3 * QUEUE_CAPACITY; ++i)
 {
 debugPrint(&q);
 puts("");
 dequeue(&q);
 enqueue(&q, (void*) i);
 }
 for (i = 0; i < QUEUE_CAPACITY; ++i)
 {
 debugPrint(&q);
 puts("");
 dequeue(&q);
 }
 while (!isEmpty(&q))
 {
 debugPrint(&q);
 dequeue(&q);
 }
 debugPrint(&q);
 return 0;
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
answered May 12, 2016 at 18:05
\$\endgroup\$
0
2
\$\begingroup\$

if (isEmpty(&pQueue)) is wrong. It should be pQueue. You also need to have the prototype in scope before you use it. Put int isEmpty(Queue* pQueue) at the top of your file.

answered May 12, 2016 at 17:02
\$\endgroup\$
0
\$\begingroup\$

If this queue header is to be used in multiple source files, then you should mark all the functions static to avoid multiple-definition errors from the linking stage.

Another option would be to put the functions in a .c file and have just the function prototypes in the header. Then you can use it in multiple places but have only one copy of the functions in the final executable.

answered May 14, 2016 at 19:27
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.