Spotlight
Spotlight

Reputation: 1299

Void** as parameter requires cast

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);
}

Upvotes: 3

Views: 355

Answers (3)

David Ranieri
David Ranieri

Reputation: 41017

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

Upvotes: 2

nalzok
nalzok

Reputation: 16107

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.

Upvotes: 2

atturri
atturri

Reputation: 1153

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.

Upvotes: 4

Related Questions