Ответ 1
void (*f) (void)
означает указатель на функцию без аргументов, возвращающих void.
void *(*f)(void *)
означает указатель на функцию, принимающую указатель void и возвращающий указатель на void.
Поскольку типы разные, компилятор не позволит вам передавать один на другой без кастования. (Обратите внимание, что кастинг на самом деле не является правильным ответом здесь, а как @detly указывает, приводит к поведению undefined.)
Что касается указателей разыменования функций, вам не нужно явно помещать "*" перед указателем функции, чтобы вызвать его. Например, вы можете вызвать указатель на функцию f, выполнив
f();
Пример указателя функции
Скажем, у вас есть функция f
, которую вы хотите передать функции takes_a_function
.
takes_a_function
, вероятно, будет иметь тип типа
void takes_a_function(void (*f)(void *data), void *data);
Обратите внимание, что есть два аргумента для takes_a_function
, указатель на функцию и указатель на void для некоторых данных. Также обратите внимание, что функция f
принимает в качестве аргумента указатель void. Идея состоит в том, что вы можете передать данные на takes_a_function
, и она пройдет ее до f
. Например, takes_a_function
может быть определен как
void takes_a_function(void (*f)(void *), void *data) {
f(data);
}
Теперь напишите функцию, чтобы перейти к takes_a_function
. Наша функция будет просто распечатывать int, который передается ему.
void prints_an_int(void *data) {
// The idiom for converting a void pointer to another kind
// of pointer. NO NEED TO CAST. Note this behavior is only
// defined if the pointer data really does point to an int.
int *i = data;
printf("%d", *i);
}
int i = 0;
takes_a_function(prints_an_int, &i);
Несколько ключевых моментов в этом примере:
-
prints_an_int
имеет тот же тип, что и указатель функции, ожидаемыйtakes_a_function
. Не нужно бросать. - Нет необходимости использовать оператор
&
для создания ссылки на функцию. Вот почему мы можем напрямую передатьprints_an_int
вtakes_a_function
. Но мы могли бы также сказатьtakes_a_function(&prints_an_int, &i)
, и было бы одинаково. -
void*
в основном означает "указатель на неизвестный тип". Чтобы на самом деле что-то с этим делать, вы должны назначить переменную типаvoid*
другой переменной-указателю, тип которой вы ожидаете. Это гарантируется, только если вы действительно перейдете к правильному типу указателя! В этом примере мы можем назначитьdata
int*
, поскольку данные действительно указывают на int. Если вам нужно больше данных, чем просто целое число, общий шаблон состоит в том, чтобы создать свой собственный тип структуры, который включает все нужные вам поля, и вместо этого передать это. - В качестве специального случая компилятор не требует от вас приведения при назначении указателей void другим указателям и наоборот. Но опять же, вы получите только определенное поведение, если вы в конечном итоге преобразуете указатель void обратно в правильный тип.