When adding a *void item into an array of *void, how does compiler know how many bytes to use in memory?

Viewed 185

I recently started learning a bit more about Data Structures and then, when implementing the Queue concept in C, I faced the following:

typedef struct {
  int max;        /* Max. enqueed items. */
  int total;      /* Total enqueued. */
  int pos_start;  /* Pos. of first item. */
  int pos_end;    /* Pos. of last item. */
  int item_size;  /* Byte size of each item. */
  void** items;   /* Array of items. */
} queue_t;

/* 
 * Omitted code for brevity purposes. 
 */

void
queue_add(queue_t* queue, void* item)
{
  if (queue_full(queue))
  {
    fprintf(stderr, "Tried to add an item into a full queue");
    exit(1);
  }

  queue->items[queue->pos_end] = item;
  queue->total++;
  queue->pos_end = find_next_end(queue);
}

Apparently it works. A couple of notes:

  1. The original tutorial taught how to create a queue of integers. I tried to challenge myself by creating a queue of "any" element - that's the reason I'm trying to use an array of void* elements.
  2. Due the fact I'm using an array of void* elements, I know that, whenever I want to use any item from this queue (e.g. in the main fuction by using queue_pop), I need to do a proper casting. By using casting, the compiler can know exactly how many bytes that variable should take in memory.

However, my big question is:

Based on my function, when I push a new item into the queue, I save it into queue->items[queue->pos_end]. If the compiler doesn't know how many bytes item has (as it's marked as a void*), how does it calculate the memory address for queue->items[queue->pos_end]?

2 Answers

the compiler doesn't know how many bytes item has (as it's marked as a void*)

The compiler knows exactly how many bytes item consists of, because its type is a pointer (to void or to int, or to another pointer, or to anything else - doesn't matter), and all pointers are of the same size (normally 4 or 8 bytes).

Pointers are always complete types including the (possibly cv-qualified) pointer type void * or void **.

From the C Standard (6.2.5 Types, p.#20)

— A pointer type may be derived from a function type or an object type, called the referenced type. A pointer type describes an object whose value provides a reference to an entity of the referenced type. A pointer type derived from the referenced type T is sometimes called ‘‘pointer to T’’. The construction of a pointer type from a referenced type is called ‘‘pointer type derivation’’. A pointer type is a complete object type.

Here is a demonstrative program.

#include <stdio.h>

int main(void) 
{
    printf( "sizeof( void * ) = %zu\n", sizeof( void * ) );

    return 0;
}

Its output might be

sizeof( void * ) = 8

So if you have an array with the element type void * then the compiler uses the value 8 in the pointer arithmetic relative to accessing elements of the array.

Related