2

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]?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Stanley Sathler
  • 368
  • 2
  • 12

2 Answers2

2

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).

ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • 1
    *all pointers are of the same size* Pedantically, [all pointers do not have to have the same size](https://port70.net/~nsz/c/c11/n1570.html#6.2.5p28). See https://stackoverflow.com/questions/36645660/why-cant-i-cast-a-function-pointer-to-void for one example question. – Andrew Henle Mar 26 '20 at 18:15
2

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.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    Does it mean that if I have an array of `void*` (8 bytes) to store integers (4 bytes), I'll be "wasting" 4 bytes in each position? Or the fact that the item is a "pointer to void" means I'll always have to push pointers to integers rather than integers themselves (which, in this case, means I won't be wasting bytes). – Stanley Sathler Mar 26 '20 at 18:24
  • 1
    @StanleySathler If you declared an array of pointers then you should store pointers. – Vlad from Moscow Mar 28 '20 at 22:26