I was testing the linked list examples in the "Mastering Algorithms with C (By Kyle Loudon)"
In the example, there was an error in the operation of the function using the void** parameter. The operation error is as below code.
code:
#include <stdio.h>
void myswap(void **val1, void **val2)
{
void **val3;
*val3 = *val1;
*val1 = *val2;
*val2 = *val3;
}
int main()
{
int val1 = 10;
int val2 = 20;
printf("%d, %d\n", val1, val2);
myswap((void **)&val1, (void **)&val2);
printf("%d, %d\n", val1, val2);
return 0;
}
result:
10, 20
0, 10 <- must be 20, 10
This seems to be an error caused by the difference between the size of the variable and the size of the pointer. This can be solved by using a long variable instead of an int variable or by creating a padding variable between the two variables in main(). (I use a 64bit system. So, the size of pointers and long types is 64 bits.)
However, I want to know how to solve it without modifying the variables, structures, and functions of the already written examples. if it is possible.
If anyone knows about this issue, please help
CodePudding user response:
You cant dereference void pointers. It is a GCC extension and underlying type is unsigned char. To get the integer you need pointer of the correct type.
You need a pointer to pass a pointer to it. In my example I use compound literal
void myswap(void **val1, void **val2)
{
int val3;
int **i1 = (int **)val1;
int **i2 = (int **)val2;
val3 = **i1;
**i1 = **i2;
**i2 = val3;
}
int main()
{
int val1 = 10;
int val2 = 20;
printf("%d, %d\n", val1, val2);
myswap(&(void *){&val1}, &(void *){&val2});
printf("%d, %d\n", val1, val2);
return 0;
}
You do need double pointers at all. If you tried to create universal swap function:
void myswap(void *val1, void *val2, size_t size)
{
unsigned char val3[size];
memcpy(val3, val1, size);
memcpy(val1, val2, size);
memcpy(val2, val3, size);
}
int main(void)
{
int val1 = 10;
int val2 = 20;
double d1 = 1.0;
double d2 = 2.0;
printf("%d, %d\n", val1, val2);
myswap(&val1, &val2, sizeof(val1));
printf("%d, %d\n\n", val1, val2);
printf("%f, %f\n", d1, d2);
myswap(&d1, &d2, sizeof(d1));
printf("%f, %f\n", d1, d2);
return 0;
}
You can swap any objects with it. Arrays, structs, unions etc etc.
CodePudding user response:
Built with gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
, your myswap
function throws a segmentation fault when dereferencing val3
that is not initialized (cf. *val3 = *val1
statement).
If myswap
's goal is to make val1
points on what val2
pointed to (and the opposite), then the code should be:
void myswap(void **val1, void **val2)
{
void *tmp;
tmp = *val1;
*val1 = *val2;
*val2 = tmp;
}
This way, your code prints the expected 10, 20
and 20, 10
messages.
However, it still ends with a "*** stack smashing detected ***: terminated
" and abort (core dumped)
error messages.
This is due to the wrong way of passing val1
and val2
arguments to myswap
function.
The myswap
function considers val1
(resp. val2
) as a double pointer variable, something that point to a void *
address (itself pointing to something else).
In the main
context, &val1
is something that points to an int
variable, and not a void *
content!
So when myswap
runs a *val1 = ...
operation, it gets problematic if sizeof(int)
and sizeof(void*)
are different (which is the case on x86_64 architecture).
So, there are 3 things you could do:
- Use the
intptr_t
(fromstddint.h
) instead ofint
, becauseintptr_t
has the same size asvoid *
.
But this won't work for non-integer variables (i.e.int main() { intptr_t val1 = 10; intptr_t val2 = 20; printf("%ld, %ld\n", (signed long)val1, (signed long)val2); myswap((void **)&val1, (void **)&val2); printf("%ld, %ld\n", (signed long)val1, (signed long)val2); return 0; }
struct
,float
...), where you cannot guarantee that variable size is the same asvoid *
- Use intermediate pointers, and switching those:
But this keepint main() { int val1 = 10; int val2 = 20; int * ptr1 = &val1; int * ptr2 = &val2; printf("%d, %d\n", *ptr1, *ptr2); myswap((void **)&ptr1, (void **)&ptr2); printf("%d, %d\n", *ptr1, *ptr2); return 0; }
val1
andval2
variables unchanged, which might not be what you want. - Use
memcpy
andsizeof
, as @0___________ is suggesting.