почему это приведение к пустому указателю действительно? - PullRequest
2 голосов
/ 31 июля 2011

Я отлаживаю программу из книги.Программа работает, но я не понимаю ни одной строки, которую я комментирую ниже.

#include <pthread.h>
#include <stdio.h>
/* Compute successive prime numbers (very inefficiently). Return the
Nth prime number, where N is the value pointed to by *ARG. */
void* compute_prime (void* arg)
{
int candidate = 2;
int n = *((int*) arg);
while (1) {
int factor;
int is_prime = 1;
/* Test primality by successive division. */
for (factor = 2; factor < candidate; ++factor)
if (candidate % factor == 0) {
is_prime = 0;
break;
}
/* Is this the prime number we’re looking for? */
if (is_prime) {
if (--n == 0)
/* Return the desired prime number as the thread return value. */
return (void*) candidate;    // why is this casting valid? (candidate is not even a pointer)
}
++candidate;

}
return NULL;
}
int main ()
{
pthread_t thread;
int which_prime = 5000;
int prime;
/* Start the computing thread, up to the 5,000th prime number. */
pthread_create (&thread, NULL, &compute_prime, &which_prime);
/* Do some other work here... */
/* Wait for the prime number thread to complete, and get the result. */
pthread_join (thread, (void*) &prime);
/* Print the largest prime it computed. */
printf(“The %dth prime number is %d.\n”, which_prime, prime);
return 0;
}

Ответы [ 3 ]

5 голосов
/ 31 июля 2011

Это недействительно. Это просто работает, если sizeof(int) == sizeof(void *), что происходит во многих системах.

A void * гарантированно может содержать указатели на объекты данных.

Вот C FAQ по теме.

Как целые числа конвертируются в указатели и из них? Могу ли я временно вставить целое число в указатель или наоборот?

Преобразование указателя в целое и целое в указатель определяется реализацией (см. вопрос 11.33), и больше нет любая гарантия, что указатели могут быть преобразованы в целые числа и обратно, без изменений

Заставить указатели на целые числа или целые числа на указатели никогда была хорошая практика

1 голос
/ 31 июля 2011

В то время как другие правы в том, что C оставляет результаты этого приведения , определенные реализацией , ваш код (с использованием pthreads) зависит от POSIX, для которого требуется модель памяти, в которой компилятор должен был бы выходить из него.способ сломать то, что вы делаете.Также все реальные реализации POSIX - это ILP32 или LP64, что означает, что любое значение int будет помещаться в указатель.

Хотя "некрасиво" с формальной точки зрения, используя int -to- void *приведение с аргументом потока или возвращаемым значением для передачи небольших целочисленных данных обычно является меньшим из двух зол, другой выбор - malloc(sizeof(int)) в одном потоке и free в другом, что не только существенно увеличивает затраты, но и можетбыть патологически плохим на некоторых распределителях, настроенных только для случая, когда поток распределения и поток освобождения совпадают.

Один из способов избежать обоих зол: если создатель потока будет некоторое время задерживаться ив конце концов, вызвав pthread_join, вы можете передать новому потоку указатель на объект данных в стеке создателя или в памяти, иным образом принадлежащим создателю.Затем новый поток может записать свой результат в это местоположение до его завершения.

1 голос
/ 31 июля 2011

Что вы подразумеваете под "действительным"?

Вы явно запрашиваете приведение, и язык или компилятор не остановят вас. полезно - это совсем другое дело.На самом деле, как вы говорите, candidate не является указателем и не указывает ни на что полезное.Получатель возвращаемого значения должен знать, что с ним делать (например, привести его обратно к int, хотя это не гарантирует, что это вернет вам исходное значение).

Если вам никогда не понадобитсязначение, вы можете просто вернуть 0.Просто pthread_create ожидает в качестве аргумента указатель на функцию типа void*(*)(void*), поэтому ваша функция должна возвращать void*.Если вам это не нужно, вы можете проигнорировать его и просто вернуть любое старое значение.

(В других ситуациях ваша функция потока могла бы выбрать malloc() некоторую память, заполнить ее данными результата и вернутьуказатель на этот адрес, поэтому void* в некотором смысле является «наиболее общим» типом возврата для функции C, которая должна быть настолько гибкой, насколько это возможно, не зная, как она будет использоваться.)

...