C - когда требуется лить указателя пустоты?

Я смотрю на Advanced Linux Programming от Митчелла, Олдхэма и Самуила. Я видел в разделе о pthreads что-то о указателях пустоты и кастингах, которые меня смущают.

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

pthread_create( &thread, NULL, &compute_prime, &which_prime );

Здесь, which_prime имеет тип int.

Но принимая значение, возвращаемое из потока с помощью pthread_join, они делают переменную переменной void.

pthread_join( thread, (void*) &prime );

Здесь prime имеет тип int снова.

Почему кастинг выполняется во втором экземпляре, а не в первом?

Ответ 1

Второй пример - хороший пример того, почему кастинг на void* обычно является ошибкой. Это должно быть

void *primep = ′  // no cast needed
pthread_join(thread, &primep);

потому что pthread_join принимает в качестве второго аргумента void**. void* только гарантирует, что ошибка передается компилятором, потому что void* автоматически преобразуется в void**.

Итак, когда вам нужно отправить в void* или обратно:

  • при работе с указателями, хранящимися как целые числа ((u)intptr_t);
  • при прохождении указателей на функции, которые имеют неполный прототип, и взять void* (или взять другой тип указателя, и у вас есть void*); что обычно означает функции, принимающие переменное количество аргументов, таких как printf.

Ответ 2

Не нужно указывать с или на указатель на void в C:

6.3.2.3 Указатели

1 Указатель на void может быть преобразован в указатель или из указателя на любой неполный или объект тип. Указатель на любой неполный или тип объекта может быть преобразован в указатель на voidи обратно; результат сравнивается с исходным указателем.


Исключение составляют только

  • при печати указателя с использованием спецификатора преобразования "%p", как он определен только для void *.
  • при копировании значения указателя из intptr_t или uintptr_t обратно в void *.

Ответ 3

В C, литье в void * из любого типа указателя и наоборот выполняется неявно. В втором примере нет необходимости в листинге.

(Обратите внимание, что в С++ литье любого указателя на void * также выполняется неявно (за исключением указателей на функции и указателей на функцию/элемент/метод, которые нельзя отличить от void *), но для отбрасывания требуется явное литье.)

Ответ 4

Я считаю, что тот же код был ссылка в других questions.

Ответ во второй ссылке объясняет:

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

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

Вот вопросник C по этому вопросу.

И цитируемый текст:

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

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

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

Ответ 5

В соответствии с документацией

int pthread_join(pthread_t thread, void **retval);

Итак, pthread_join принимает в качестве второго аргумента pointer to void*. Это потому, что

В pthread_join вы возвращаете адрес, переданный pthread_exit, по законченная нить. Если вы передаете простой указатель, он передается чтобы вы не могли изменить, на что указывает. Быть способным измените значение указателя, переданного на pthread_join, оно должно быть передан как сам указатель, то есть указатель на указатель.

Теперь, на ваш вопрос: "Почему кастинг выполняется во втором случае, а не в первом?" В первом случае, т.е. pthread_create, он ожидает в качестве четвертого аргумента a void*. Таким образом, передача &which_prime будет неявно преобразована в void*.

Во втором случае, т.е. pthread_join, он ожидает a void**, и мы проходим там &prime. Итак, компилятор будет жаловаться. Итак, чтобы обойти ошибку, автор передает листинг void*, который будет автоматически преобразован в void**.

Но это нехорошее решение. Даже автор цитирует:

4.1.3. Значения возврата нити

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

Решение::

void* prime ; // make prime as void*
pthread_join( thread, &prime );
printf( "%d\n", (intptr_t)prime ) ; 
// intptr_t instead of int to get an integer type 
// that the same size as a pointer

Или, если вы не хотите изменять текущую реализацию prime как int, Как, @larsmans реализованы,

void *primep = ′
pthread_join(thread, &primep);