I'm trying to pick my C skills again. I want to sum a sequence in different threads, each thread would return a pointer of the sum of a part of the sequence. However, when I tried to convert the void*
type value local_sum
to int
, problem occurred.
I tried to convert with sum += *(int*)local_sum;
, a segment error occurred and I got Process finished with exit code 11
.
I found that if I use sum += (int)local_sum;
, it would be okay. But I couldn't convince myself: shouldn't local_sum
be a void *
? Why it can be converted to int
with (int)local_sum
?
I'm so grateful it you could answer the problem.
The part that sum each process's return value is here:
int sum = 0;
for (int i = 0; i < NUM_THREADS; i ++) {
void * local_sum;
pthread_join(count_threads[i], (&local_sum));
sum += (int)local_sum;
}
The function of a thread is here:
void * count_thr(void *arg) {
int terminal = ARRAY_SIZE / NUM_THREADS;
int sum = 0;
for (int i = 0; i < terminal; i ++) {
sum += *((int*)arg + i);
}
return (void*)sum;
}
A secure and portable solution could be the use of an union:
union void_cast {
void* ptr;
int value;
};
Then for example you can safely reinterpret a void*
pointer with:
int VOID_TO_INT(void* ptr) {
union void_cast u;
u.ptr = ptr;
return u.value;
}
void* INT_TO_VOID(int value) {
union void_cast u;
u.value = value;
return u.ptr;
}
So your code can be changed to:
sum += VOID_TO_INT(local_sum);
You're return
ing the value of int sum
by setting a void *
address to it. In this case, the address is not valid. But, if you keep that in mind and get the value of sum
by casting a void *
to int
it will work.
void *
is used this way sometimes to return
either a value (e.g. int
) or an address to something (e.g. struct
).
To illustrate this:
int a = 5;
void *p = (void *)a;
int b = (int)p;
a
, p
, and b
all have a value of 5
. p
does not point to a valid address. Trying to dereference p
would result in undefined behavior:
b = *(int *)p; // Undefined Behavior!
Consider the following program:
#include <limits.h>
#include <stdio.h>
int main(void)
{
int a, b;
void *p;
a = 5;
p = (void *)a;
b = (int)p;
printf("%d %p %d\n", a, p, b);
a = INT_MAX;
p = (void *)a + 1;
b = (int)p;
printf("%d %p %d\n", a, p, b);
return 0;
}
When compiled, I get the following warnings:
$ gcc main.c -o main.exe main.c: In function ‘main’: main.c:9:9: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] p = (void *)a; ^ main.c:10:9: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] b = (int)p; ...
A warning is issued because, as pointed out by @Gerhardh, the sizeof(int)
and the sizeof(void *)
may be different. You may suffer data loss if the value of the void *
exceeds the maximum value a int
can hold.
Output
$ ./main.exe 5 0x5 5 2147483647 0x80000000 -2147483648
You can't do *(int*)local_sum
because local_sum
is not an int*
cast to void*
. local_sum
is an int
cast to void*
. It is a number reinterpreted as an address, but only for transfer purposes, because pthread_exit
only allows you to return a void*
, not an int
and because the standard explicitly allows implementation-defined conversion (6.3.2.3p5, 6.3.2.3p6) between integers and numbers as long as the values fit (if they don't then, UB). If you return, e.g., 0x42
, it is highly unlikely there's anything at address 0x42
, so you should forget about dereferencing it and instead you should convert it back to an integer ASAP, either with (int)local_sum;
or perhaps better with (int)(intptr_t)local_sum;
(though intptr_t
isn't guaranteed to exist) or (perhaps best) with (int)(intmax_t)local_sum;
so as to avoid possible compiler warnings about converting to an integer of a different size on LP64 platforms.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With