views:

39

answers:

3

I have a function which creates an array of pointers. The function which allocates the memory returns the new memory pointer through a parameter passed to the function. The simplest code which can reproduce the problem is as follows:

void foo (void** new_mem, size_t bytes)
{
    *new_mem = malloc(bytes);
}

int main (void)
{
    int** ptr_arr; // Want to create an array of pointers

    foo(&ptr_arr, sizeof(int*)*100); // Create an array size of 100
                                     // compiler emits warning: 
                                     // 'void **' differs in levels of indirection from 'int ***'

    return 0;
}

I could cast the first parameter passed to foo like so: '(void**)&ptr_arr' to get rid of the warning, however, I'm wondering: Is there a more appropriate solution?

A: 

The problem is you're taking the address of a double pointer and passing it to a double pointer parameter. Taking the address of a value creates a pointer so you end up really with a triple pointer int*** which is why you get the error.

Casting the result to void** will technically work here although it is bordering on an abuse on the type system.

JaredPar
Casting an `int ***` to `void **` is asking for trouble. If you lie to the compiler, it will get its revenge.
Philip Potter
@Philip, it's C. The compiler will enact it's revenge even if you give it candy.
JaredPar
A: 

The trouble is ptr_arr is already an int **, when you do an &ptr_arr, it will show as int *** , so what you are doing is incorrect.

So, you can just pass ptr_arr if your intention is to pass an int **

Also, I feel your code can do well with a cleanup. Your approach seems to be wrong.

Jay
except that `ptr_arr` doesn't point to anything, and `foo` assigns to `*new_mem`
Philip Potter
A: 

Although there's a guaranteed conversion from int * to void *, there's no such guarantee for converting from int ** to void **. To think about why this might be, consider that an int * might actually be smaller than a void *. As a result, using a void ** pointer to walk over an array of int *s will walk the wrong stride and get the wrong data. Furthermore, there's no guarantee that int * will use the same representation as void *, only that there is a way to convert between them.

In practice, I don't know of any machines where this will fail. But it's not guaranteed by the standard.

EDIT: eek, and what everyone says about passing an int ***. But even if you pass an int **, the above still applies.

EDIT2: the comp.lang.c FAQ has an excellent discussion about this.

Philip Potter
So it sounds like the solution is to use an intermediate 'void*' that will contain the new memory address, then set the int** to the intermediate void*.
Andrew
@Andrew: No, using an intermediate `void *` doesn't magically make the conversion valid. If it's not valid to convert between `T *` and `U *`, then it's also not valid to do `T *` to `void *` to `U *`; but C won't warn you about it.
Philip Potter