This is probably a fairly basic C question, but so far I've not found an answer to it and I'm relearning C after having not touched it for 20 or so years.
The code I'm building needs a queue which I am representing as a circular buffer. I therefore wrote a set of routines for operating on the buffer - enqueue, dequeue, etc. The routines live in their own files, Queue.c and Queue.h, for better data abstraction. I originally wrote this to operate on a single buffer, but now realize I may need more than one in my code. So the routines need to take the buffer itself as an argument (in C++ or other OOP language this would be easily done by making the queue a class and instantiating multiple copies as needed, but I'm using straight C).
My concern comes from allocating space to the buffer itself. I end up with pointers to structs which contain pointers to arrays, and I'm having trouble making sure I'm doing it right, especially in terms of memory lifetime. Here's the relevant parts of the code:
The buffer is defined, in Queue.h, as a struct:
typedef struct { uint8_t *que; //items go in through tail, come out through head uint8_t *qhead; //points at oldest bit uint8_t *qtail; //points at newest bit uint8_t *qstart; //start of queue uint8_t *qstop; //end of queue } queue_struct;
in my Main.c I statically instantiate a copy of the struct (it's marked volatile as there's a chance that it will need to be accessed from an interrupt:
static volatile queue_struct PacketQue;
The queue is set up with the following code (which lives in Queue.c):
static void QueueSetupDynamic(queue_struct *queue, uint16_t length) { queue->que = malloc( length * sizeof( uint8_t ) ); queue->qhead = queue->que; queue->qtail = queue->que; queue->qstart = (uint8_t *) queue->que; queue->qstop = (uint8_t *) queue->que + length -1; }
This is called from Main.c with an explicit cast (MAXQLEN is #defined, in this case as 512):
QueueSetup( ( queue_struct * ) &PacketQue, MAXQLEN ); //explicit cast to non-volatile
I believe this should work (it compiles, and seems to run correctly in the simulator, haven't tried to deploy it to the chip yet). I also believe that by making PacketQue volatile, it will be treated that way if called from an interrupt, even though there's the cast at QueueSetup. If I'm wrong in this, please let me know.
Now, the real question is that I'd really like to avoid using malloc. All the best practices guides I've seen for embedded programming caution against it, and it seems like there shouldn't be a need for it - the size is fixed, by MAXQLEN, at compile-time. But I can't figure out how to allocate the space.
My initial thought was something like this:
static void QueueSetupStatic(queue_struct *queue) { uint8_t temp[MAXQLEN]; queue->que = temp; queue->qhead = queue->que; queue->qtail = queue->que; queue->qstart = (uint8_t *) queue->que; queue->qstop = (uint8_t *) queue->que + MAXQLEN -1; }
But I believe that the compiler will stop protecting that chunk of memory once "temp" goes out of scope. I can declare it static, but that seems cludgy - it's not really a static variable of the function.
I can also declare a static array in Main.c, but again, that seems inelegant - data abstraction principles suggest that it belongs in Queue.c/ somewhere, not in Main.c
Ideas much appreciated.