Search code examples
cpointersvoid-pointersindirection

Void pointers pretending to be void double pointers


I've been doing some thinking. I haven't found anything directly answering this question, but I think I know the answer; I just want some input from some more experienced persons.

Knowns:

A void pointer points to just a memory address. It includes no type information.

An int pointer points to a memory address containing an int. It will read whatever is in the memory address pointed to as an integer, regardless of what was stuffed into the address originally.

Question:

If a void double pointer void ** foo were to point to a dynamically allocated array of void pointers

void ** foo = malloc(sizeof(void *) * NUM_ELEMENTS);

is it true, as I am supposing, that because of the unique nature of void pointers actually lacking any sort of type information that instead of void ** foo an equivalent statement would be

void * bar = malloc(sizeof(void *) * NUM_ELEMENTS);

and that when I use indirection to access by assigning a specific type, such as with

(It was pointed out that I can't dereference void pointers. For clarity to the purpose of the question the next line is changed to be appropriate to that information)

int ** fubar = bar;

that I would get an appropriate pointer from the single void pointer which is just acting like a double pointer?

Or is this all just in my head?


Solution

  • It is permissible to assign the result of malloc to a void * object and then later assign it to an int ** object. This is because the return value of malloc has type void * anyway, and it is guaranteed to be suitable for assignment a pointer to any type of object with a fundamental alignment requirement.

    However, this code:

    #define NUM_ELEMENTS 1000
    void *bar = malloc(sizeof(void *) * NUM_ELEMENTS);
    int **fubar = bar;
    *fubar = 0;
    

    is not guaranteed by the C standard to work; it may have undefined behavior. The reason for this is not obvious. The C standard does not require different types of pointers to have the same size. A C implementation may set the size of an int * to one million bytes and the size of a void * to four bytes. In this case, the space allocated for 1000 void * would not be enough to hold one int *, so the assignment to *fubar has undefined behavior. Generally, one would implement C in such a way only to prove a point. However, similar errors are possible on a smaller scale: There are C implementations in which pointers of different types have different sizes.

    A pointer to an object type may be converted to a pointer to another object type provided the pointer has alignment suitable for the destination type. If it does, then converting it back yields a pointer with the original value. Thus, you may convert pointers to void * to pointers to void and back, and you may convert pointers to void * to pointers to int * and back, provided the alignments are suitable (which they will be if the pointers were returned by malloc and you are not using custom objects with extended alignments).

    In general, you cannot write using a pointer to an object type and then read the same bytes using a pointer to a different object type. This violates aliasing rules. An exception is that if one of the pointers is to a character type. Also, many C implementations do support such aliasing, but it may require setting command-line options to enable such support.

    This prohibition on aliasing includes reinterpreting pointers. Consider this code:

    int a;
    int *b = &a;
    void **c = (void **) &b;
    void *d = *c;
    int *e = (int *) d;
    

    In the fourth line, c points to the bytes that b occupies but *c tries to interpret those bytes as a void *. This is not guaranteed to work, so the value that d gets is not necessarily a pointer to a, even when it is converted to int * as in the last line.