2

I have been reading up on casting allocations in C (c11) and wonder what changes when it becomes a pointer-pointer

Say I have a function

void** foo( someInput )

which allocates and instantiates one of many types of pre-typedef'd structs. Why does;

MyStructA **mystructa = foo(...);

result in the compiler warning Incompatible pointer types passing ... to type void**

This causes me to cast the allocation, which seems to be generally frowned upon with single-pointers.
MyStructA **mystructa = (MyStructA**) foo(...someInput..);
MyStructB **mystructb = (MyStructB**) foo(...someOtherInput...);

Just looking for a little insight from the community, and didn't see any previous discussion on this.

sam
  • 488
  • 8
  • 21
  • Your cast in the example is wrong. – ouah Apr 20 '15 at 21:02
  • Ah I didn't see that, thanks. Thats the problem with arbitrary examples – sam Apr 20 '15 at 21:05
  • `void **` is almost never correct. If you have a function that needs to return a pointer to more than one kind of object--even a pointer to other pointers--it should return `void *`, meaning "address of something unspecified. – Lee Daniel Crocker Apr 20 '15 at 21:19
  • I encounter it when I need to return a null-terminated,unknown length, list of pointers to structures which could be a handful of types based upon the input parameters. I think its usage here is quite warranted, and the question is more about the syntax of C rather than my specific usage. – sam Apr 20 '15 at 21:24

2 Answers2

3

In C, a pointer-to-void may be converted to or from a pointer to any object type without a cast. Note, however that void ** is not a pointer-to-void. It is a pointer to a void *. Hence converting it to a different pointer-to-pointer-to-object like MyStructA ** requires an explicit cast.

The correct solution is to change the return type of foo to void *

void *foo( someInput )

Then the return value of foo can be used without a cast, e.g.

MyStructA **mystructa = foo(...);

Here's a complete example

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int a;
    int b;
}
MyStructA;

void *foo( int a, int b, int count )
{
    int i;
    MyStructA **array = malloc( count * sizeof(MyStructA *) );
    for ( i = 0; i < count; i++ )
    {
        array[i] = malloc( sizeof(MyStructA) );
        array[i]->a = a + i;
        array[i]->b = b + i;
    }

    return( array );
}

int main( void )
{
    int i;
    int count = 4;
    MyStructA **array = foo( 5, 21, count );
    for ( i = 0; i < count; i++ )
        printf( "%d %d %d\n", i, array[i]->a, array[i]->b );
    /* freeing memory is left as an exercise for the reader */
    return 0;
}
user3386109
  • 34,287
  • 7
  • 49
  • 68
  • I was almost swayed that this was the correct method for what Im after. However, `foo()` needs to be able to instantiate one of many pre-typedefed structs. So, while you example always works when allocating `MyStructA`, it does not support instantiating `MyStructB`. – sam Apr 20 '15 at 22:33
  • @sam True, I was just giving a simple example to show how casting works (since that was the question `:)` ). How to instantiate one of many structs is an entirely different question, focused mainly on the proper specification of `someinput`. – user3386109 Apr 20 '15 at 22:59
  • You are correct +1, and thanks for the example. It got me thinking about the approach I'm using, but in essence `void* foo()` is the appropriate idea. It can be just be confusing, at least for me, when the `void*` is pointing to other `void*`. – sam Apr 21 '15 at 16:12
  • @sam Yup, confusing at first, but powerful. Learn to use the `void` wisely and a master of the C language you will become :-) – user3386109 Apr 21 '15 at 21:08
0

void pointers are generic pointers. So that they can be treated correctly you need to perform the appropriate typecasting.

For example in the comparison function for qsort it is expected precisely this behavior. And this is a general phenomenon and this is what you should do (contrary to what you imply as being something frowned upon).

Effectively, one uses void pointers when one can use the same code for different data structures. However, so that the data structures can be treated correctly, the compiler needs to know how to interpret the data, which is done by typecasting.

Further, regarding your statement, I would expect to see code of the form:

MyStructA **mystructa = (MyStructA **) foo( ... );
MightyMouse
  • 13,208
  • 8
  • 33
  • 43
  • 1
    It's not a `void *`, it's a `void **`. `void **` contrary to `void *` is not a generic pointer. And regarding `void *` you don't need to cast. – ouah Apr 20 '15 at 21:10