Пример №1

Указатели на функции

Прежде чем вводить указатель на функцию, напомним, что каждая функция характеризуется типом возвращаемого значения, именем, количеством, порядком следования и типами параметров.

При использовании имени функции без последующих скобок и параметров имя функции выступает в качестве указателя на эту функцию, и его значением служит адрес размещения функции в памяти. Это значение адреса может быть присвоено другому указателю, и затем уже этот новый указатель можно применять для вызова функции. Однако в определении нового указателя должен быть тот же тип, что и возвращаемое функцией значение, то же количество, порядок следования и типы параметров. Указатель на функцию определяется следующим образом:

тип функции (*имя_указателя)(спецификация_параметров);

Например: int(*func1ptr) (char); — определение указателя func1ptr на функцию с параметром типа char, возвращающую значение типа int.

Если приведенную синтаксическую конструкцию записать без первых круглых скобок, то есть в виде int *fun(char); то компилятор воспримет ее как прототип некой функции с именем fun и параметром типа char, возвращающей значение указателя типа int *.

Второй пример: char * (*func2Ptr) (char *, int); - определение указателя func2Ptr на функцию с параметрами типа указатель на char и типа int, возвращающую значение типа указатель на char.

В определении указателя на функцию тип возвращаемого значения и сигнатура (типы, количество и последовательность параметров) должны совпадать с соответствующими типами и сигнатурами тех функций, адреса которых предполагается присваивать вводимому указателю при инициализации или с помощью оператора присваивания. В качестве простейшей иллюстрации — небольшой пример:

/*Пример определения и использования указателей на функции*/ #include<iostream.h> // описание функции f1void f1(void) { cout << " Выполняется f1()";} // описание функции f2void f2(void){ cout << " Выполняется f2()";} void main(){ // объявление указателя на функцию, void (*ptr)(void); // ptr - указатель на функцию ptr = f2; // ptr инициализируется адресом f2() (*ptr)(); // вызов f2() по ее адресу ptr = f1; // присваивается адрес f1() (*ptr)(); // вызов f1() по ее адресу ptr(); // вызов эквивалентен (*ptr)();}

Результат выполнения программы:

Выполняется f2()Выполняется f1()Выполняется f1()

В программе описан указатель ptr на функцию, и ему последовательно присваиваются адреса функции f2 и fl. Обратите внимание на форму вызова функции с помощью указателя на функцию:

(*имя_укаэателя)(список_фактических_параметров);

Значением имени_указателя служит адрес функции, а с помощью операции разыменования * обеспечивается обращение по адресу к этой функции. Однако будет ошибкой записать вызов функции без скобок в виде *ptr(); Дело в том, что операция () имеет более высокий приоритет, нежели операция обращения по адресу *. Следовательно, в соответствии с синтаксисом будет вначале сделана попытка обратиться к функции ptr(). И уже к результату будет отнесена операция разыменования, что будет воспринято как синтаксическая ошибка.

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

При присваивании указателей на функции также необходимо соблюдать соответствие типов возвращаемых значении функции и сигнатур для указателей правой и левой частей оператора присваивания. То же справедливо и при последующем вызове функций с помощью указателей, т.е. типы и количество фактических параметров, используемых при обращении к функции по адресу, должны соответствовать формальным параметрам вызываемой функции.

Следующая программа иллюстрирует гибкость механизма вызовов функций с помощью указателей.