Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Question about converting `void *` to `int` in C

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;
}
like image 570
Frost-Lee Avatar asked Nov 16 '18 15:11

Frost-Lee


3 Answers

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);
like image 193
Morpheus Avatar answered Oct 24 '22 06:10

Morpheus


You're returning 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
like image 10
Fiddling Bits Avatar answered Oct 24 '22 05:10

Fiddling Bits


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.

like image 8
PSkocik Avatar answered Oct 24 '22 05:10

PSkocik