Программирование на C и C++

Онлайн справочник программиста на C и C++

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

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

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

Адрес функции получается при использовании имени функции без каких-либо скобок или аргументов. (Очень похоже на массивы, где адрес получается с использованием имени массива без индексов.) Рассмотрим следующую программу, предназначенную для демонстрации нюансов объявления:

#include <stdio.h>
#include <string.h>
void check(char *a, char *b, int (*cmp) (const char *, const char *));
int main(void)
{
char s1 [80], s2[80];
int (*p) (const char*, const char*);
p = strcmp; /* получение адреса strcmp() */
gets(s1);
gets (s2);
check(s1, s2, p);
return 0;
}

void check (char *a, char *b, int (*cmp) (const char *, const char *))
{
printf("Testing for equality.\n");
if(!(*cmp) (a, b)) printf("Equal");
else printf("Not equal");
}

Когда вызывается check(), ей передаются два указателя на символы и один указатель на функцию. В функции check() аргументы объявляются как указатели на символы и указатель на функцию. Надо обратить внимание на способ объявления указателя на функцию. Следует использовать аналогичный метод при объявлении других указателей на функцию, за исключением тех случаев, когда отличается возвращаемый тип или передаваемые параметры. Скобки вокруг *cmp необходимы для правильной интерпретации компилятором данного выражения.

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

Рассмотрим работу функции strcmp() в функции check(). Оператор

if (!(*cmp) (a, b) ) printf("Equal");

осуществляет вызов функции, в данном случае strcmp(), с помощью cmp, который указывает на данную функцию. Вызов происходит с аргументами a и b. Данный оператор демонстрирует общий вид использования указателя на функцию для вызова функции, на которую он указывает. Круглые скобки вокруг *cmp необходимы вследствие наличия приоритетов. На самом деле можно напрямую использовать cmp, как показано ниже:

if (!cmp(a, b)) printf("Equal");

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

check(s1, s2, strcmp);

Данный оператор устраняет необходимость наличия дополнительной переменной-указателя.

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

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
void check(char *a, char *b, int (*cmp) (const char *, const char *));
int numcmp (const char *a, const char *b) ;
int main(void)
{
char s1[80], s2 [80];
gets (s1);
gets (s2);
if(isalpha(*s1))
check (s1, s2, strcmp);
else
check(s1, s2, numcmp);
return 0;
}

void check(char *a, char *b, int (*cmp) (const char *,const char *))
{
printf("Testing for equality.\n");
if(!(*cmp) (a, b)) printf ("Equal");
else printf("Hot equal");
}

int numcmp (const char *a, const char *b)
{
If(atoi(a)==atoi(b)) return 0;
else return 1;
}