Вопрос о преобразовании `void *` в `int` в C - PullRequest
0 голосов
/ 16 ноября 2018

Я снова пытаюсь выбрать свои навыки Си.Я хочу суммировать последовательность в разных потоках, каждый поток будет возвращать указатель суммы части последовательности.Однако когда я попытался преобразовать значение типа 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 голосов
/ 16 ноября 2018

Вы не можете сделать *(int*)local_sum, потому что local_sum не является int* приведением к void*. local_sum - это int приведение к 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.

0 голосов
/ 16 ноября 2018

Безопасным и переносимым решением может быть использование объединения:

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);
0 голосов
/ 16 ноября 2018

Вы return вводите значение int sum, устанавливая для него адрес void *. В этом случае адрес недействителен. Но, если вы помните об этом и получите значение sum, приведя void * к int, это будет работать.

void * используется таким образом иногда для return либо значения (например, int), либо адреса чего-либо (например, struct).

Чтобы проиллюстрировать это:

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

a, p и b имеют значение 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
...