繁体   English   中英

关于在C中将`void *`转换为`int`的问题

[英]Question about converting `void *` to `int` in C

我想再次选择我的C技能。 我想在不同的线程中对一个序列求和,每个线程将返回一个序列的一部分之和的指针。 但是,当我尝试将void*类型值local_sum转换为int ,出现了问题。

我试着用sum += *(int*)local_sum; ,发生了一个段错误,我Process finished with exit code 11Process finished with exit code 11

我发现如果我使用sum += (int)local_sum; ,没关系。 但我无法说服自己: local_sumlocal_sum是一个void * 为什么可以使用(int)local_sum将其转换为int

我很感激你能回答这个问题。

将每个进程的返回值相加的部分在这里:

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

线程的功能在这里:

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

您将通过为其设置void *地址来return int sum的值。 在这种情况下,地址无效。 但是,如果你记住这一点并通过将void *int来获得sum的值,那么它将起作用。

void *以这种方式使用有时return值(例如int )或地址(例如struct )。

为了说明这一点:

int a = 5;
void *p = (void *)a;
int b = (int)p;

apb都具有值5 p不指向有效地址。 尝试取消引用p会导致未定义的行为:

b = *(int *)p; // Undefined Behavior!

考虑以下程序:

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

编译时,我收到以下警告:

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

...

发出警告是因为,正如@Gerhardh所指出的, sizeof(int)sizeof(void *)可能不同。 如果void *的值超过int可以容纳的最大值,则可能会丢失数据。

产量

$ ./main.exe
5 0x5 5
2147483647 0x80000000 -2147483648

你不能做*(int*)local_sum因为local_sum不是int* cast to void* local_sum是一个intvoid* 这是重新解释为地址的数量,但仅用于传输目的,因为pthread_exit只允许你返回void* ,而不是int因为标准中明确允许实现定义的转换( 6.3.2.3p56.3.2.3p6 )在整数和数字之间,只要值适合(如果它们不适合,UB)。 如果你返回,例如, 0x42 ,那么地址0x42处的任何东西都不太可能,所以你应该忘记取消引用它,而应该将它转换回整数ASAP,或者使用(int)local_sum; 或者更好用(int)(intptr_t)local_sum; (虽然不保证intptr_t存在)或(也许最好)与(int)(intmax_t)local_sum; 以避免在LP64平台上有关转换为不同大小的整数的可能的编译器警告。

安全便携的解决方案可能是使用联合:

union void_cast {
    void* ptr;
    int value;
};

然后,例如,您可以安全地重新解释void*指针:

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

所以你的代码可以改为:

sum += VOID_TO_INT(local_sum);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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