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

时间:2018-11-16 15:20:43

标签: c pointers type-conversion pthreads

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

我尝试用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;
}

线程的功能在这里:

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

3 个答案:

答案 0 :(得分:10)

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

void *有时用于return值(例如int)或某个地址的地址(例如struct)。

为了说明这一点:

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

apb的值均为5p没有指向有效地址。尝试取消引用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

答案 1 :(得分:8)

您不能执行*(int*)local_sum,因为local_sum不是强制转换为int*的{​​{1}}。 void*local_sum的强制转换。它是一个重新解释为地址的数字,但仅出于转移目的,因为int仅允许您返回void*,而不是pthread_exit,并且该标准明确允许实现定义的转换(6.3.2.3p56.3.2.3p6),只要值适合就可以(在整数和数字之间)(如果不适用,则为UB)。如果返回,例如void*,则在地址int上几乎没有任何东西,因此您应该忘记对其取消引用,而应该将其转换回整数,或者使用{{1 }}或使用0x42更好(尽管不能保证0x42存在)或(也许最好)使用(int)local_sum;,以避免编译器警告将其转换为a的整数在LP64平台上大小不同。

答案 2 :(得分:0)

一种安全且可移植的解决方案可以是使用联合:

mike@tictac:~/Projects/conserve-with-us/conserve-with-us-api$ curl https://registry-1.docker.io/v2/
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]}

然后,例如,您可以使用以下命令安全地重新解释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;
}
相关问题