Разработчики C решили именовать свои типы таким образом, чтобы сценарий использования типа максимально совпадал с тем, каким образом вы используете этот тип для получения значения. Так, например, когда у вас есть объявление типа
int a;
Вы можете просто сказать a
, чтобы получить int
. Если у вас есть тип как
int *a;
Затем вы должны разыменовать a
, написав *a
, чтобы получить int
.
Вы можете использовать аналогичную, хотя и более сложную, логику для декодирования опубликованных вами типов. Давайте начнем с
void (*a)(int x, int y)
Это говорит о том, что если вы разыграете a
(написав *a
), то у вас останется нечто, похожее на
void (int x, int y)
Функция принимает два int
с и возвращает void
. Другими словами, вы можете думать о a
как о указателе на функцию; после разыменования вы возвращаете функцию.
Теперь для этого зверя:
void (*b(int x, int y))(int)
Этот хитрее. Идея заключается в следующем. Если вы возьмете b
и передадите ему два аргумента, вы получите что-то похожее на это:
void (*)(int)
Указатель на функцию, принимающую int
и возвращающую void
. Другими словами, b
- это функция, которая принимает два аргумента, затем возвращает указатель на функцию, который принимает один аргумент и возвращает void
.
Несколько сложно декодировать эти типы, поэтому часто вам не кажется, что они написаны таким образом, и вместо этого вы используете typedef
для упрощения вещей. Например, этот typedef:
typedef void (*FunctionTakingTwoInts)(int, int);
Говорит, что вы можете использовать FunctionTakingTwoInts
для определения указателя на функцию, которая указывает на функцию, которая принимает два int
с и возвращает void
. Отсюда объявление a
упрощается до
FunctionTakingTwoInts a;
Аналогично, в случае b
, давайте определим тип
typedef void (*FunctionTakingOneInt)(int);
Теперь мы можем переписать b
как
FunctionTakingOneInt b(int x, int y);
Из чего, я думаю, гораздо яснее, что на самом деле означает тип.
Надеюсь, это поможет!