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

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

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

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

В некоторых типах программ пользователь может выбирать некоторый вариант из списка возможных действий. Например, в системе подсчета может быть меню более чем с 20 вариантами. Когда выбор сделан, программа выполняет соответствующую функцию. Это может быть сделано двумя способами. Как правило, с этой целью используется оператор switch. Тем не менее в приложениях, где требуется высокая производительность, лучше применять другой способ. Можно использовать массив указателей, содержащих адреса функций. Выбор, сделанный пользователем, декодируется и используется для индексации массива указателей, вызывая выполнение необходимой функции. Этот метод гораздо быстрее, чем метод использования switch.

Для того, чтобы понять, как можно использовать массив указателей на функции вышеприведенным способом, представим, что необходимо создать простейшую систему инвентаризации, позволяющую вводить, удалять и просматривать данные, а также выходить в операционную систему. Если функции, выполняющие эти действия, называются соответственно enter(), del(), review() и quit(), то следующий фрагмент кода корректно инициализирует массив указателей на функции:

void enter(void), del(void), review(void), quit(void); int menu(void);
void (*options[])(void) = {
enter,
del,
review,
quit
};

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

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

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
void enter(void), del(void), review(void), quit(void);
int menu(void);
void (*options []) (void) = {
enter,
del,
review,
quit
};

int main(void)
{
int i;
i = menu(); /* получение выбора пользователя */
(*options[i])(); /* выполнение */
return 0;
}

int menu(void)
{
char ch;
do {
printf("1. Enter\n");
printf("2. Delete\n");
printf("3. Review\n");
printf("4. Quit\n");
printf("Select a number: ");
ch = getche();
printf ("\n");
} while(!strchr("1234", ch));
return ch-49; /* преобразование к целочисленному эквиваленту */
}

void enter(void)
{
printf("In enter.");
}

void del(void)
{
printf("In del.");
}

void review(void)
{
printf("In review.");
}

void quit (void)
{
printf("In quit.");
exit(0);
}

Программа работает следующим образом. Сначала выводится меню и пользователь вводит число, соответствующее его выбору. Поскольку введенное число представлено в ASCII-коде, то необходимо вычесть 49 (ASCII-код цифры 0) для получения двоичного числа. Данное значение затем возвращается в main() и используется как индекс массива options - массива указателей на функцию. После этого вызывается необходимая функция.

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