3

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);
}
Spotlight
  • 1,279
  • 1
  • 13
  • 28
  • 2
    `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 Apr 14 '16 at 07:53
  • 1
    @AlterMann How about I want to change the allocation for `arr`? – Sourav Ghosh Apr 14 '16 at 07:54
  • @SouravGhosh That's exactly what I need to accomplish. – Spotlight Apr 14 '16 at 07:55
  • 1
    I think you may learn a few things from [**this** question](https://stackoverflow.com/questions/18551785/is-it-legal-to-modify-any-data-pointer-through-a-void) and its answers. Don't just skim it. It's worth the time. – WhozCraig Apr 14 '16 at 07:58
  • @SouravGhosh, then, you need to dereference what is passed inside the function, `*(int **)param = newaddress;` – David Ranieri Apr 14 '16 at 07:58
  • @AlterMann and if I don't know the type of the array? – Spotlight Apr 14 '16 at 08:01
  • @Spotlight, if you don't know the type you can use a chunk of bytes a la `qsort`, use `char *` as generic pointer instead of `void *` – David Ranieri Apr 14 '16 at 08:03
  • @WhozCraig I really understand that using void** can lead to errors, but not necessarily – Spotlight Apr 14 '16 at 08:03
  • How does it make sense to declare a function that takes an array of unspecified type, without passing the size of the data? – Lundin Apr 14 '16 at 08:07
  • @Spotlight then answer me this: You said, "I have a function in C that needs to receive a pointer to an array (with an unspecified type)." - Is said mysterious need requiring you modify the (a) pointer to the array itself, (b) the data the array points *to* , or (c) both? The unspecified-clause already means you're going to simply pass a `void*` (which will take any object address). The only difference in those three options is what the *function* does with that `void*` it receives. If you can't answer that question concretely, you're putting your cart before the horse. – WhozCraig Apr 14 '16 at 08:07
  • @WhozCraig I need to modify the pointer to the data (a). – Spotlight Apr 14 '16 at 08:09
  • No matter what else, `void*` is the correct parameter type, as Alter said in the opening comment. All that then matters is how take that `void*` and convert it to whatever pointer-to-pointer type you're interested in, and all of that happens in the *function*. – WhozCraig Apr 14 '16 at 08:14
  • @WhozCraig Ok got it thanks. I don't quite like it, since the caller must know he has to pass a pointer of a pointer and can't know that by simply reading the signature of the function – Spotlight Apr 14 '16 at 08:17
  • @Spotlight in order for `f` to use the array, you're going tohave to tell it about the array's elements in some way or another – M.M Apr 14 '16 at 09:06

3 Answers3

4

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.

atturri
  • 1,133
  • 7
  • 13
2

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.

nalzok
  • 14,965
  • 21
  • 72
  • 139
  • There is no implicit conversion from `int**` to `void**`, OP's code is a constraint violation – M.M Apr 14 '16 at 09:05
  • That code is a constraint violation because there is no such thing as implicit conversion between `int**` and `void **` – M.M Apr 14 '16 at 10:35
  • @M.M But seems that this is allowed by the standard, see: http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7 – nalzok Apr 14 '16 at 14:01
  • 6.2.7p2 doesn't seem related. 6.3.2.3p7 says the conversion is possible, but other parts say that it must be an explicit conversion (cast) – M.M Apr 15 '16 at 11:37
  • Look up matching arguments to parameters (which defers to assignment operator) – M.M Apr 15 '16 at 14:38
2

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
David Ranieri
  • 39,972
  • 7
  • 52
  • 94