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);
}
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.
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.
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
Related
I was trying to pass an array to a method. I tried following ways:
func2(ARRAY_NAME, length) => WORKS, no warning
func2(&ARRAY_NAME[0], length) => WORKS, no warning
func2(&ARRAY_NAME, length) => WORKS, but WITH WARNING
I dont understand why the last one (#3) gives warning. &ARRAY_NAME works without warnings in memset, memcpy etc. Why its a problem in custom method?
WARNING message:
functionTest.c:35:11: warning: passing argument 1 of ‘func2’ from incompatible pointer type [-Wincompatible-pointer-types]
35 | func2(&temp, ARRAY_SIZE);
| ^~~~~
| |
| unsigned char (*)[200]
functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
8 | void func2(unsigned char* buf, int length)
CODE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define ARRAY_SIZE 200
void func2(unsigned char* buf, int length)
{
// Change data of any index
buf[0] = 100;
buf[10] = 200;
}
void func1()
{
unsigned char temp[ARRAY_SIZE];
// Initialize
memset(temp, 0, sizeof(temp));
for(int i = 0; i < ARRAY_SIZE; i++)
{
printf("\t%d", temp[i]);
}
printf("\n-----------------------------------------------\n");
printf("Address : %p\n", &temp);
printf("Address of 0th index : %p\n", &temp[0]);
printf("\n-----------------------------------------------\n");
// Pass array in func2
func2(&temp, ARRAY_SIZE); // WARNING
func2(&temp[0], ARRAY_SIZE); // NO WARNING
for(int i = 0; i < ARRAY_SIZE; i++)
{
printf("\t%d", temp[i]);
}
printf("\n-----------------------------------------------\n");
}
int main()
{
func1();
return 0;
}
As it is clear written in the error message
functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
8 | void func2(unsigned char* buf, int length)
the function expects a pointer of the type unsigned char * but the argument expression &temp has the type unsigned char ( * )[200] and there is no implicit conversion from one pointer type to another though values of the pointers are the same: the address of the extent of memory occupied by the array.
As for the functions memset and memcpy then they deal with pointers of the type void *. For example the function memset has the following declaration
void *memset(void *s, int c, size_t n);
And a pointer to object of other type can be implicitly converted to pointer of the type void *.
From the C Standard (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.
func2 is declared to have a first parameter of type unsigned char *, so it should be passed a pointer to a type compatible with unsigned char or a pointer to void, which will be automatically converted to unsigned char *.
With func2(temp, ARRAY_SIZE), the array temp is automatically converted to a pointer to its first element. This pointer is an unsigned char *, so it satisfies the requirements.
With func2(&temp[0], ARRAY_SIZE), temp[0] is an unsigned char, so &temp[0] is a pointer to an unsigned char. This satisfies the requirements.
With func2(&temp, ARRAY_SIZE), &temp is a pointer to an array of 200 elements of unsigned char. It points to the same place as &temp[0], bit its type is different. It is a pointer to an array, not a pointer to unsigned char or to a compatible type nor a pointer to void. So it does not satisfy the requirements, and the compiler complains.
Pointers to unsigned char and pointers to arrays are different. If the type of pu is unsigned char * and the type of pa is unsigned char (*)[200] (a pointer to an array of 200 unsigned char), then adding 1 to pu, as in pu + 1, produces a pointer to the next unsigned char after pu, but adding 1 to pa produces a pointer to the next array after pa. In other words, pu + 1 points to the next byte in memory, but pa + 1 points 200 bytes further along in memory.
One purpose of C’s type system is to help avoid errors. When a non-matching type is passed to a function, the programmer may be expected some behavior other than what the language defines. So the compiler issues a diagnostic message.
With memset, its first parameter is declared to be void *. void is an incomplete type; it acts as a placeholder for other types. memset is designed to work on the bytes of any object, so it accepts a pointer to any object type. When any pointer to an object type is passed for a void * parameter, it is automatically converted to void *, without any diagnostic message.
I am quite new to C, and have been practicing with pointers. I understand that a void * pointer accepts a pointer of any kind, so this works fine:
void functionWithSingleVoidPointer(void *simplePointer)
{
free(simplePointer);
}
int main()
{
char *simplePointer = malloc(sizeof *simplePointer);
functionWithSingleVoidPointer(simplePointer); //No warnings
return 0;
}
However, I don't understand why this:
void functionWithDoubleVoidPointer(void **doublePointer)
{
free(*doublePointer);
}
int main()
{
char *simplePointer = malloc(sizeof *simplePointer);
char **doublePointer = &simplePointer;
functionWithDoubleVoidPointer(doublePointer);
}
gives me a warning:
warning: passing argument 1 of ‘functionWithDoubleVoidPointer’ from incompatible pointer type [-Wincompatible-pointer-types] ... note: expected ‘void **’ but argument is of type ‘char **’
Why doesn't the casting from char * to void * give any warning, but the casting from char ** to void ** does?
A char* value points to a char object; a void* object can point to any kind of object. The conversion is safe (as long you don't do anything unsafe with the result).
A char** pointer value must point to a pointer object of type char* somewhere in memory. A void** pointer value must point to an object of type void*. Since char* and void* are different types, you can't just assume that you can safely treat an object of one type as if it were an object of the other type. (Most likely they have the same representation, but the language doesn't guarantee that.)
A void** is not a void pointer, that's it.
void** is a pointer to a void pointer, which only accept void pointers.
As I am a beginner in C
When I run the following C code :
#include<stdio.h>
void f(int *p)
{
*p=3;
}
int main()
{
int *p;
f(&p);
return 0;
}
I get these messages after compilation:
1) warning:passing arg 1 of 'f' from incompatible pointer type f(&p)
2) note: expected 'int *' but argument is of type 'int **' void
f(int *p)
Please note:
&p is the memory address of a variable
*p is the actual value of the variable
So what you are doing is:
In main: You are generating a variable pointer p. And you are passing this pointers address to a function f.
f thinks it gets a pointer as parameter but it gets a memory address and obviusly can't handle it.
More information is here
Hope I could help!
You're passing a pointer to a pointer as an argument to a function expecting a pointer to an int. They're two different types and the compiler will complain.
There is another issue where you have the 'pointer' to the int but not the space allocated to store it. There are a couple solutions to this situation.
1)
{
int q; // add a new int, to which p points to. this allocates memory for an int
int *p = &q;
f(p); // don't pass a pointer to a pointer, just the pointer
return 0;
}
2)
{
int p; // have p be the int in the first place, not a pointer
f(&p); // your original call is unchanged and works now
return 0;
}
It's hard to think about this, because there are multiple levels of pointers.
Here's a simpler example. This is what we might call "pass by reference":
#include <stdio.h>
void f(int *ip)
{
*ip = 5;
}
int main(){
{
int i = 7;
printf("%d\n", i);
f(&i);
printf("%d\n", i);
}
Function f accepts a pointer to an int. It modifies that int. In main, we have an int variable i. We take the address of that variable using &, and pass the resulting pointer to function f. In this way, function f is able to modify i in the caller. Note that function f takes a pointer to something, and that what it takes a pointer to is the type of i in the caller -- in other words, pointer to int.
Now let's look at another example. This time, instead of manipulating an int by reference, we're going to manipulate a pointer variable, a char *. (We're going to end up dealing with pointers to pointers.)
void g(char **sp)
{
*sp = malloc(10);
strcpy(*sp, "oranges");
}
int main(){
{
char *s = "apples";
printf("%s\n", s);
g(&s);
printf("%s\n", s);
}
Again, function g accepts an argument by reference. Again, function g is able to modify a variable in its caller. Again, the pointer type accepted by g is a pointer to the type being manipulated. Since the type being manipulated is "pointer to char", function g accepts a pointer to pointer to char, or char **.
But the code you posted is sort of a mixture of the two. Your function f accepts an int pointer, just like mine, and it tries to use that int pointer to modify an int variable in the caller. But then, in your main function, the variable you're trying to modify isn't an int, it's a pointer to int, or int *.
When you call
f(&p);
you're starting with a pointer-to-int, p, and you're taking its address with &, resulting in a pointer-to-pointer-to-int. But then you;re calling function f, which expects a pointer-to-int. That's what the compiler is trying to tell you with the messages
warning: passing arg 1 of 'f' from incompatible pointer type
note: expected 'int *' but argument is of type 'int **'
In a function that asks a double pointer like this one:
#include <stdlib.h>
void prueba(void **ap)
{
*ap = NULL;
}
compiled with the following main:
#include <stdlib.h>
#include <stdio.h>
void prueba(void **ap);
int main(void)
{
char *str;
str = (char *)malloc(sizeof(char) * 5);
prueba(&str);
if (str == NULL)
printf("NULL");
return (0);
}
I get this warning with gcc:
main2.c:11:9: warning: incompatible pointer types passing 'char **' to parameter of type 'void **' [-Wincompatible-pointer-types]
prueba(&str);
^~~~
main2.c:4:23: note: passing argument to parameter 'ap' here
void prueba(void **ap);
But if i do the same but with simple pointers, that is give a pointer to char to a function who asks for a void pointer, gcc compiles without warnings, for example, the following compiles right:
function:
#include <stdlib.h>
void prueba(void *ap)
{
ap = NULL;
}
main:
#include <stdlib.h>
#include <stdio.h>
void prueba(void *ap);
int main(void)
{
char *str;
str = (char *)malloc(sizeof(char) * 5);
prueba(str);
if (str == NULL)
printf("NULL");
return (0);
}
So my question is, why it works with simple pointers but it does not work with double pointers (or greater i guess)?
According to the C Standard (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.
So in the second code snippet a pointer of the type char * is implicitly converted to the type void * though the function does not make sense because it deals with a copy of its argument and therefore does not changes the original pointer.
However the type void ** is not the same as the type void *. So the quote is not applicable for the type void ** and the compiler issues a diagnostic message because there is no implicit conversion from a pointer to any object type to a pointer of the type void **.
A void * can freely be assigned to or from any object pointer freely. This freedom does not extends to void **, as that is a distinct type.
Also, your fixed program won't work as expected, since ap = NULL; only changes the local variable inside of prueba and not the parameter passed in.
Change your original program to accept a char ** instead of a void **.
This code is practice code for pointers. But I am not understanding the (int**)&p; means in this code.
void fun(void *p);
int i;
int main()
{
void *vptr;
vptr = &i;
fun(vptr);
return 0;
}
void fun(void *p)
{
int **q;
q = (int**)&p;
printf("%d\n", **q);
}
Please elaborate how it is evaluated.
It's a type cast, that interprets the value of &p, which has type void **, as instead having type int ** which is the type of the variable the value is stored in.
The cast is necessary, since void ** is not the same as void * and does not automatically convert to/from other (data) pointer types. This can be confusing.
&p being of type void**, is being casted to type of int** which is to be assigned to q.
SIDE-NOTE : "Any pointer can be assigned to a pointer to void. It can then be cast back to its original
pointer type. When this happens the value will be equal to the original pointer value."
Be careful when using pointers to void. If you cast an arbitrary
pointer to a pointer to void, there is nothing preventing you from
casting it to a different pointer type.