Explain difference between the use of void pointer - c

My code needs to pass an array to a void pointer (The struct has (void *) that cannot be modified). The two versions of code below produce the same output but the latter has two warnings. My question is which of the two methods is preferred? Is there a way to typecast to remove the warnings?
This version does not have warnings and produces the output as expected:
#include <stdio.h>
void test(void *var_arr, char var_1);
typedef struct {
char chip;
void *buffer;
}test_struct;
int main()
{
int test_array[3] = {3,7,5};
char var_1 = 0x20;
printf("Hello, World!\n");
test(&test_array, var_1);
return 0;
}
void test(void *var_arr, char var_1)
{
int i;
test_struct var_ts;
var_ts.chip = var_1;
var_ts.buffer = var_arr;
for (i=0; i<3; ++i)
printf("\nThe data values are : %X \n\r", *((int *)var_ts.buffer+i));
}
Hello, World!
The data values are : 3
The data values are : 7
The data values are : 5
This version below has two warnings but compiles and produces the output expected:
Warning(s):
source_file.c: In function ‘main’:
source_file.c:17:10: warning: passing argument 1 of ‘test’ from incompatible pointer type
test(&test_array, var_1);
^
source_file.c:3:6: note: expected ‘int ’ but argument is of type ‘int ()[3]’
void test(int *var_arr, char var_1);
#include <stdio.h>
void test(int *var_arr, char var_1);
typedef struct {
char chip;
void *buffer;
}test_struct;
int main()
{
int test_array[3] = {3,7,5};
char var_1 = 0x20;
printf("Hello, World!\n");
test(&test_array, var_1);
return 0;
}
void test(int *var_arr, char var_1)
{
int i;
test_struct var_ts;
var_ts.chip = var_1;
var_ts.buffer = (void *)var_arr;
for (i=0; i<3; ++i)
printf("\nThe data values are : %X \n\r", *((int *)var_ts.buffer+i));
}

The lower version tells you what the problem was in the first one: passing a pointer to the array instead of a pointer to the first element.
Passing a pointer to the array, which has the type int(*)[3]:
test(&test_array, var_1);
Passing a pointer to the first element, which has the type int*:
test(test_array, var_1);
The code happens to work because the two pointers points to the same address, so the pointer to the array appears to work, but the code is still undefined.
Passing a pointer to the first element is correct, as the array test_array, which has the type int[3] decays to type int* when it is passed to the function.

The warnings are shown because the compiler does static analysis on the code and identified a possible cause of bugs.
When using the void pointer the compiler cannot do this static analysis since it does not know what type is used.
If the datatype is known I would always prefer using this type as pointer instead of the void pointer. I would only use the void pointer when it is acceptable that the pointer can point to anything. In the end this is about personal taste and the code guidelines you follow.

Related

converting a string to a void pointer

I'm trying to figure out how to "transform" strings (char*) to void* and viceversa.
When I execute this my output is just the first printf and ignores the second one, it doesn't even write "after = "
PS This little program is just to understand, I know i could actually use swap(&s[0],&s[1]). I need to know how to properly cast a void pointer into an array of strings.
I'm working on a uni project where I need to create my own quick_sort algorythm and I need the swap function inside of it to work with void pointers.
#include <stdio.h>
#include <stdlib.h>
static void swap(char** x,char** y);
static void swap(char** x,char** y){
char* temp=*x;
*x=*y;
*y=temp;
}
int main()
{
char* s[2];
s[0]="weee";
s[1]="yooo";
void* array=s;
printf("before %s %s\n",s[0],s[1]);
swap((&array)[0],(&array)[1]);
printf("after = %s %s",(char*)array,(char*)array);
return 0;
}
I think I'm missing something big
Thanks in advance :D
In this declaration the array s used as an initializer is implicitly converted to a pointer to its first element of the type char **.
void* array = s;
In the call of the function swap
swap((&array)[0],(&array)[1]);
the first argument can be the pointer array itself that will be implicitly casted to the pointer type of the corresponding parameter
swap( array, (&array)[1]);
But you need to correctly pass the second argument. To do this you need to cast the pointer array explicitly like
swap( array, ( char ** )array + 1 );
In the call of printf you need also correctly to supply argument expressions.
Here is your updated program
#include <stdio.h>
static void swap(char** x,char** y);
static void swap(char** x,char** y){
char* temp=*x;
*x=*y;
*y=temp;
}
int main()
{
char* s[2];
s[0]="weee";
s[1]="yooo";
void* array=s;
printf("before %s %s\n",s[0],s[1]);
swap( array, ( char ** )array + 1 );
printf("after = %s %s", *(char**)array, ( (char**)array )[1]);
return 0;
}
The program output is
before weee yooo
after = yooo weee
void *array = s; declares array to be a void *. Then &array is the address of that void *, so &array[1] would access a void * after it. But there is no void * after it, since void *array defines a single void *.
array could be properly defined to alias s with char **array = s;, after which swap(&array[0], &array[1]); would work as desired.
If you define array as void **array = (void **) s;, then swap(&array[0], &array[1]); will produce diagnostic messages because the types are wrong. You could use swap((char **) &array[0], (char **) &array[1]);.
Then, if you print the strings with printf("after = %s %s", array[0], array[1]);, this will work, although it is not entirely proper code. Using array[0] as an argument passes a void * where printf is expecting a char * for the %s. However, the C standard guarantees that void * and char * have the same representation (encode their values using bytes in memory in the same way), and it further says (in a non-normative note) that this is intended to imply interchangeability as arguments to functions.
The void* doesn't seem to fulfil any particular purpose here, just swap the pointers: swap(&s[0],&s[1]);.
You could also do this:
char** ptr = &s[0];
printf("before %s %s\n",ptr[0],ptr[1]);
swap(&ptr[0],&ptr[1]);
printf("after = %s %s",ptr[0],ptr[1]);
If you for reasons unknown insist on using void* then note that as your code stands, it points at the first char* in your array of char*. However, it isn't possible to perform pointer arithmetic on void* since that would entail knowing how large a "void" is. The void* doesn't know that it points at an array of pointers. Therefore array[i] is nonsense.
Also, the void* are set to point at char* so you simply cannot pass it to a function expecting a char**. You'd have to rewrite the whole program in a needlessly obfuscated way, so just abandon that idea.

Incompatible pointer types (double pointers)

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 **.

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

output of the function with the float pointer parameter and input as a address of int variable

I am very new to this topic pointers in C. I have one code as follow.
The output of this code is 0.000000 but i can't understand why so?
void foo(float *);
int main()
{
int i=10,*p=&i;
foo(&i);
}
void foo(float *p)
{
printf("%f",*p);
First check this site to know pointer behavior.
In your code you declare float type parameter but pass integer value.
Try this one:
#include <stdio.h>
void foo(int *p)
{
printf("%d",*p);
}
int main()
{
int i=10,*p=&i;
foo(&i);
return 0;
}
It gives The below warning.
test.c:6:2: warning: passing argument 1 of ‘foo’ from incompatible pointer type [enabled by default]
Because, you are passing the address to the foo function but expected argument is float *.
Then you are defining the p pointer as a integer type.
The variable p does not points the i.
But in foo function calling, there is no address is passed.
So, It gives warning and 0.0000.
So, change your code like below.
void foo(float *);
int main()
{
flat i=10,*p=&i; //Change the data type
foo(&i);
}
void foo(float *p)
{
printf("%f",*p);
}
Now the output is
10.0000

Segmentation fault error in gcc

Why the following code is giving segmentation fault error
#include<stdio.h>
int main()
{
int i;
int a[2][2]={1,2,3,4};
int **c;
c=a;
for(i=0;i<4;i++)
printf("%d",*(*(c)+i));
}
This assignment:
c=a;
Should give you a warning. a decays into a pointer to its first element, which has type int (*)[2]. Assigning that type to a variable of type int ** requires an explicit cast.
Redeclaring c should fix your problem:
int (*c)[2];
Example warning from clang:
example.c:8:6: warning: incompatible pointer types assigning to 'int **' from
'int [2][2]' [-Wincompatible-pointer-types]
c=a;
^~
1 warning generated.
Read the comments of the following code:
#include<stdio.h>
int main()
{
int i;
int a[2][2]={{1,2},{3,4}}; // Put each dimension in its braces
/*int a[2][2]={1,2,3,4};
This declaration of array make the following:
a1[ONE] a2[TWO] THREE FOUR
a3[Unknown value] a4[Unknown value]
i.e. the numbers 3 and 4 are being written beyond of the array...
*/
int *c1;
int **c2; // `int **` is a pointer to a pointer, so you have to
c1=&a[0][0]; // declare a pointer `c1` and then assign to it `c2`.
c2=&c1; // AND use `&` to assing to pointer the address of
// variable, not its value.
for(i=0;i<4;i++)
printf("%d",*(*(c2)+i)); // here is `double dereference` so here must be `c2`
// (ptr-to-ptr-to-int) but not c1 (ptr-to-int).
return 0; // AND make the `main()` to return an `int` or
// make the returning type `void`: `void main(){}`
// to make the `main()` function to return nothing.
}
This is a problem in the definition of c. int **c; Suggests that this is a pointer to a pointer, but the definition of a is of type int *[2]. Changing the definition of c to int (*c)[2] should do the trick.

Resources