简体   繁体   中英

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

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 (eg int ) or an address to something (eg 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, eg, 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM