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

Fro*_*Lee 10 c pointers pthreads type-conversion

我想再次选择我的C技能.我想在不同的线程中对一个序列求和,每个线程将返回一个序列的一部分之和的指针.然而,当我试图转换void*类型值local_sumint,发生了问题.

我试图转换sum += *(int*)local_sum;,发生了一个段错误,我得到了Process finished with exit code 11.

我发现,如果我使用sum += (int)local_sum;,那就没关系.但我无法说服自己:不应该local_sum是一个void *?为什么它可以转换为int(int)local_sum

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

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

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;
}
Run Code Online (Sandbox Code Playgroud)

线程的功能在这里:

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;
}
Run Code Online (Sandbox Code Playgroud)

Fid*_*its 10

您可以通过设置地址来return获得价值.在这种情况下,地址无效.但是,如果你记住这一点,并通过铸造它来获得它的价值将起作用.int sumvoid *sumvoid *int

void *这种方式有时用于return某个值(例如int)或某个地址(例如struct).

为了说明这一点:

int a = 5;
void *p = (void *)a;
int b = (int)p;
Run Code Online (Sandbox Code Playgroud)

a,pb所有都有价值5. p不指向有效地址.尝试取消引用p会导致未定义的行为:

b = *(int *)p; // Undefined Behavior!
Run Code Online (Sandbox Code Playgroud)

考虑以下程序:

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

$ 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 *超过a int可以容纳的最大值,则可能会丢失数据.

产量

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

  • 请注意,这是实现定义的行为 (2认同)

PSk*_*cik 8

你不能这样做,*(int*)local_sum因为local_sum不是int*演员void*.local_sumint演员void*.这是重新解释为地址的数量,但仅用于传输目的,因为pthread_exit只允许你返回void*,而不是int因为标准中明确允许实现定义的转换(6.3.2.3p5,6.3.2.3p6整数和数字之间)只要值适合(如果它们不适合,则UB).如果你返回,例如,0x42地址是不可能的0x42,所以你应该忘记取消引用它,而是你应该尽快将它转换回整数,或者用(int)local_sum;或者更好用(int)(intptr_t)local_sum;(虽然intptr_t不能保证存在)或者(可能是最好的)(int)(intmax_t)local_sum;以避免在LP64平台上转换为不同大小的整数时可能的编译器警告.