На этом шаге мы начнем рассматривать описание и использование указателей на функции.
Прежде чем вводить указатель на функцию, напомним, что каждая функция характеризуется типом возвращаемого значения, именем и сигнатурой. Напомним, что сигнатура определяется количеством, порядком следования и типами параметров. Иногда говорят, что сигнатурой функции называется список типов ее параметров. При использовании имени функции без последующих скобок и параметров имя функции выступает в качестве указателя на эту функцию, и его значением служит адрес размещения функции в памяти. Это значение адреса может быть присвоено другому указателю, и затем уже этот новый указатель можно применять для вызова функции. Однако в определении нового указателя должен быть тот же тип, что и возвращаемое функцией значение, и та же сигнатура. Указатель на функцию определяется следующим образом:
тип_функции (*имя_ухазателя)(спецификация_параметров);
Например: int (*func1Ptr) (char); - определение указателя func1Ptr на функцию с параметром типа char, возвращающую значение типа int.
Если приведенную синтаксическую конструкцию записать без первых круглых скобок, т.е. в виде int *fun (char); то компилятор воспримет ее как прототип некой функции с именем fun и параметром типа char, возвращающей значение указателя типа int *.
Второй пример: char * (*func2Ptr) (char * ,int); - определение указателя func2Ptr на функцию с параметрами типа указатель на char и типа int, возвращающую значение типа указатель на char.
В определении указателя на функцию тип возвращаемого значения и сигнатура (типы, количество и последовательность параметров) должны совпадать с соответствующими типами и сигнатурами тех функций, адреса которых предполагается присваивать вводимому указателю при инициализации или с помощью оператора присваивания. В качестве простейшей иллюстрации сказанного приведем программу с указателем на функцию:
//RAZN1_1.СРР - определение и использование указателей на функции. #include <iostream.h> // Для ввода-вывода. void f1(void) // Определение f1. { cout << "\nВыполияется f1()"; } void f2(void) // Определение f2. { cout << "\nВыполняется f2()"; } void main() { void (*ptr)(void); // ptr - указатель на функцию. ptr = f2; // Присваивается адрес f2(). (*ptr)(); // Вызов f2() по ее адресу. ptr = f1; // Присваивается адрес f1(). (*ptr)(); // Вызов f1() по ее адресу. ptr(); // Вызов эквивалентен (*ptr)(); }
Результат выполнения программы:
Выполняется f2() Выполняется f1() Выполняется f1()
В программе описан указатель ptr на функцию, и ему последовательно присваиваются адреса функций f2 и f1. Заслуживает внимания форма вызова функции с помощью указателя на функцию:
(*имя_указателя)(список_фактических_параметров);
Здесь значением имени_указателя служит адрес функции, а с помощью операции разыменования * обеспечивается обращение по адресу к этой функции. Однако будет ошибкой записать вызов функции без скобок в виде *ptr();. Дело в том, что операция () имеет более высокий приоритет, нежели операция обращения по адресу *. Следовательно, в соответствии с синтаксисом будет вначале сделана попытка обратиться к функции ptr(). И уже к результату будет отнесена операция разыменования, что будет воспринято как синтаксическая ошибка.
На следующем шаге мы продолжим рассматривать указатели на функции.