2
votes

I have a function in C that needs to receive a pointer to an array (with an unspecified type).

To do so, I use void**, as I would use void* to receive an array of unspecified elements.

There's a problem unfortunately: the compiler gives a warning (passing argument 1 of 'f' from incompatible pointer type). If I ignore the warning, and try to execute the program, everything works as expected.

The only way to get rid of the warning is to cast whatever I try to pass to the function to void**.

Why does C behaves like that? And is there a better way to solve the warning?

PS: I need to compile using GCC with the flags -std=gnu89 -pedantic -Wall

Example

int f(void** param){ return 1; }

int main(){
    int *arr = malloc(sizeof(int) * 20);
    int i;
    for(i=0; i < 20; i++) arr[i] = i;
    f(&arr);
}
3
int f(void** param){ return 1; } --> int f(void* param){ return 1; }, don't use **, you can read a pointer to an array using a single pointer (void *) - David Ranieri
@AlterMann How about I want to change the allocation for arr? - Sourav Ghosh
@SouravGhosh That's exactly what I need to accomplish. - Spotlight
I think you may learn a few things from this question and its answers. Don't just skim it. It's worth the time. - WhozCraig
@SouravGhosh, then, you need to dereference what is passed inside the function, *(int **)param = newaddress; - David Ranieri

3 Answers

3
votes

The pointer to anything type is void*, and the compiler will not complain about conversions to that type. But void** is not a pointer to anything, it's a pointer to an array of pointers to anything, which is quite different from a pointer to an array of pointers to integers, so the compiler complains. So, to solve the warning, yes you would need to cast explicitly.

2
votes

It seems that you want to modify the address of the data (not the value) inside the function, you can't do that directly with a void * because you can't use arithmetic with void *, but you can pass the size of the first element and a chunk of bytes (char *), suppose you want to change the address of arr to arr + 1 (the second element of the array) inside the function:

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

void f(void *ptr, size_t size)
{
    // char *p = *ptr;  Wrong, you can't dereference a void * without a cast
    char *p = *(char **)ptr; /* pointer to address of ptr */

    memmove(p, p + size, size); /* assign the address of ptr + 1 to ptr */
}

int main(void)
{
    int *arr = malloc(sizeof(int) * 20);
    int i;
    for (i = 0; i < 20; i++) arr[i] = i;
    f(&arr, sizeof arr[0]);
    printf("%d\n", arr[0]);
    return 0;
}

Output:

1
1
votes

Although void * is the "generic pointer" in C, void ** isn't a "generic pointer to pointer". Instead, it's nothing more than the "specific pointer to void *, the generic pointer".

In your case, a int ** is converted implicitly to a void **, which is not generic. Since a void ** is not guaranteed to be able to hold all pointer variables (thus incompatible to a int **), the compiler raises a warning.

Here is the warning generated by clang:

main.c:7:7: warning: incompatible pointer types passing 'int **' to parameter of
      type 'void **' [-Wincompatible-pointer-types]
    f(&arr);
      ^~~~
main.c:1:14: note: passing argument to parameter 'param' here
int f(void** param){ return 1; }

To eliminate this warning, you can have int f(void* param);, and cast param to int ** inside the function. There will be no warning because a void * can be used to store any pointer (Quoted from N1570):

6.3.2.3 Pointers

1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.