Implementing a generic Queue in C I wrote a generic Queue that could with work any data type you give it. The Queue can do any the basic operations that you would expect a Queue can do such as enqueue, dequeue, peek and so on.

I would like you to critique me on:


My general style
My ability to properly deallocate memory so that there is no memory leaks
Properly handling memory allocation failure
Anything else that you would like to add


Queue.h

#ifndef QUEUE_H_INCLUDED
#define QUEUE_H_INCLUDED

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

typedef struct QueueList
{
    int sizeOfQueue;
    size_t memSize;
    node *head;
    node *tail;
}Queue;

void queueInit(Queue *q, size_t memSize);
int enqueue(Queue *, const void *);
void dequeue(Queue *, void *);
void queuePeek(Queue *, void *);
void clearQueue(Queue *);
int getQueueSize(Queue *);

#endif /* QUEUE_H_INCLUDED */


Queue.c

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include "Queue.h"

void queueInit(Queue *q, size_t memSize)
{
   q-&gt;sizeOfQueue = 0;
   q-&gt;memSize = memSize;
   q-&gt;head = q-&gt;tail = NULL;
}

int enqueue(Queue *q, const void *data)
{
    node *newNode = (node *)malloc(sizeof(node));

    if(newNode == NULL)
    {
        return -1;
    }

    newNode-&gt;data = malloc(q-&gt;memSize);

    if(newNode-&gt;data == NULL)
    {
        free(newNode);
        return -1;
    }

    newNode-&gt;next = NULL;

    memcpy(newNode-&gt;data, data, q-&gt;memSize);

    if(q-&gt;sizeOfQueue == 0)
    {
        q-&gt;head = q-&gt;tail = newNode;
    }
    else
    {
        q-&gt;tail-&gt;next = newNode;
        q-&gt;tail = newNode;
    }

    q-&gt;sizeOfQueue++;
    return 0;
}

void dequeue(Queue *q, void *data)
{
    if(q-&gt;sizeOfQueue &gt; 0)
    {
        node *temp = q-&gt;head;
        memcpy(data, temp-&gt;data, q-&gt;memSize);

        if(q-&gt;sizeOfQueue &gt; 1)
        {
            q-&gt;head = q-&gt;head-&gt;next;
        }
        else
        {
            q-&gt;head = NULL;
            q-&gt;tail = NULL;
        }

        q-&gt;sizeOfQueue--;
        free(temp-&gt;data);
        free(temp);
    }
}

void queuePeek(Queue *q, void *data)
{
    if(q-&gt;sizeOfQueue &gt; 0)
    {
       node *temp = q-&gt;head;
       memcpy(data, temp-&gt;data, q-&gt;memSize);
    }
}

void clearQueue(Queue *q)
{
  node *temp;

  while(q-&gt;sizeOfQueue &gt; 0)
  {
      temp = q-&gt;head;
      q-&gt;head = temp-&gt;next;
      free(temp-&gt;data);
      free(temp);
      q-&gt;sizeOfQueue--;
  }

  q-&gt;head = q-&gt;tail = NULL;
}

int getQueueSize(Queue *q)
{
    return q-&gt;sizeOfQueue;
}


TestQueue.c

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include "Queue.h"

int main()
{
    int val;
    Queue q;

    queueInit(&amp;q, sizeof(int));

    for(val = 0; val &lt; 10; val++)
    {
        enqueue(&amp;q, &amp;val);
        printf("The value %d has been enqueued.\n", val + 1);
    }

    printf("\n");

    queuePeek(&amp;q, &amp;val);

    printf("The value that is at the front of the queue is %d\n\n", val + 1);

    while(getQueueSize(&amp;q) &gt; 0)
    {
        dequeue(&amp;q, &amp;val);
        printf("%d has been dequeued.\n", val + 1);
    }

    return 0;
}


Output
Pattern-less function names.  For Queue.h, rather see function named  obviously make sense together like Queue_Init(), Queue_GetSize() than queueInit(), getQueueSize(), etc.
Comments with # preprocessing may not be portable 

// #endif /* QUEUE_H_INCLUDED */
#endif

There is no need for a head and tail.  Alternative, only store tail and have the tail point to the head of the list.  End of list detected when p-&gt;next == tail-&gt;next.  This makes your head node one field smaller.  Important if code uses lots of queues.
Unclear why code uses int for the queue size type.  A signed type is not needed (could use unsigned) and on a system where size_t could be much wider than int, a queue size like size_t is more prudent.  Robust code would check for a queue exceed the max value of size in enqueue().
Good use of size_t for memSize.  Good error checking for malloc().  Good to have test case.  IMO, a commented sample usage in the *.h file is nice. ; the *.h being the public interface to your good code.
Pedantic: Robust would check for memSize==0 in queueInit() as that negates the correctness of the malloc() checks which should be if(newNode-&gt;data == NULL &amp;&amp; q-&gt;memSize &gt; 0).
A little documentation goes a long way.  suggest a line or two of comment preceding each function declaration in Queue.h.
Functions like queuePeek(Queue *q, void *data) that do not alter *q should be declared queuePeek(const Queue *q, void *data).  This self documents the unchanging nature of q in the function to users and allows some optimizations a compiler may not otherwise employ.  It is a check on the implementation of the function too.
For completeness, suggest q-&gt;memSize = 0 in clearQueue().
Change #include order.  Put "Queue.h" first as a check that Queue.h does not depend on the 3 &lt;*.h&gt; include files - unless that .h file is coded in Queue.h.

#include "Queue.h"
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
// #include "Queue.h"

Indicate failure.  If void dequeue(Queue *q, void *data) does not have anything in the queue to copy to data, there is no indication of that here.  Perhaps return true/false indicating success.  Same for queuePeek().
For debugging,  zero filling memory before free() I have found useful.  Errant code tends to fail faster with a 0 pointer/data than with its original data still potentially intact.  Faster failing code is easier to debug. YMMV.
Opinion:  Storing the queue size is of dubious value, unless of course that is the reason for the queue type - one with a quick size report.  Alternative, drop the size field and calculate when needed.  More often, I have found the need for bool Queue_IsEmpty(const Queue *q) sufficient than needing a quick size and prefer to drop the ever present size field.