void ToUpper(char *); void ToLowertchar *); void (*pf) (char *); char mis[] = "Nina Metier"; pf = ToUpper;
(*pf)(mis); // применить ToUpper к mis (первый синтаксис) pf = ToLower;
pf(mis); // применить ToLower к mis (второй синтаксис)
Каждый подход выглядит логичным. Проанализируем первый подход: так как pf указывает на функцию ToUpper, то *pf — это функция ToUpper, поэтому выражение (*pf) (mis) аналогично ToUpper (mis). Чтобы убедиться в эквивалентности ToUpper и (*pf), достаточно взглянуть на объявления ToUpper и pf. Второй подход можно объяснить так: из-за того, что имя функции является указателем, указатель и имя функций можно применять взаимозаменяемо, следовательно, pf (mis) — это то же самое, что и ToLower (mis). Чтобы удостовериться в эквивалентности pf и ToLower, просто посмотрите на оператор присваивания для pf. Исторически сложилось так, что разработчики С и Unix в Bell Labs избрали первый подход, а разработчики, которые расширяли Unix в Беркли, приняли второй подход. Компилятор K&R С не разрешает вторую форму, но для поддержки совместимости с существующим кодом стандарт ANSI С принимает обе формы ((*pf) (mis) и pf (mis)) как эквивалентные. Последующие стандарты сохранили такой в высшей степени двойственный подход.
Одним из наиболее распространенных случаев использования указателей на данные является аргумент функции, и то же самое относится к указателю на функцию. Например, рассмотрим следующий прототип функции:
void show(void (* fp) (char *), char * str);
Он выглядит запутанным, но в нем объявляются два параметра, fp и str. Параметр fp — это указатель на функцию, a str — указатель на данные. Точнее, fp указывает на функцию, которая принимает параметр char * и имеет возвращаемый тип void, а str указывает на char. Таким образом, имея представленные выше объявления, можно делать вызовы функций вроде приведенных ниже:
show(ToLower, mis); /* show() использует функцию ToLower(): fp = ToLower */ show(pf, mis); /*show() использует функцию, указанную посредством pf: fp = pf */
Каким образом show() применяет переданный указатель на функцию? Для вызова этой функции в show() используется либо синтаксис fp(), либо синтаксис (* fр)():
void show(void (* fp)(char *), char * str)
{
(*fp)(str); /* применить выбранную функцию к str */ puts(str); /* отобразить результат */
}
Здесь функция show() сначала трансформирует строку str, применяя к ней функцию, на которую указывает fp, после чего отображает результирующую строку.
Структуры и другие формы данных 615
Кстати говоря, функции с возвращаемыми значениями могут использоваться двумя разными способами при передаче в качестве аргументов другим функциям. Например, взгляните на следующие операторы:
functionl(sqrt); /* передает адрес функции sqrt */
function2(sqrt(4.О)); /* передает возвращаемое значение функции sqrt */
Первый оператор передает адрес функции sqrt ( ) , и предположительно functionl() будет применять эту функцию в своем коде. Второй оператор сначала вызывает функцию sqrt() и затем передает возвращаемое значение (в этом случае 2.0) функции function2().
Для демонстрации основных идей в листинге 14.16 используется функция show() вместе с набором функций трансформации в качестве аргументов. В листинге также продемонстрировано несколько полезных приемов поддержки меню.
Листинг 14.16. Программа func_ptr.c

616 Глава 14

Структуры и другие формы данных 617

Ниже показаны результаты пробного запуска:
Введите строку (пустая строка - выход из программы):
Does С make you feel loopy?
Введите выбранный вариант из меню: u) нижний регистр 1) верхний регистр
t) поменять местами регистры о) исходный регистр n) следующая строка t
dOES с MAKE YOU FEEL LOOPY?
Введите выбранный вариант из меню: u) нижний регистр 1) верхний регистр
t) поменять местами регистры о) исходный регистр n) следующая строка l
does с make you feel loopy?
Введите выбранный вариант из меню: u) нижний регистр 1) верхний регистр
t) поменять местами регистры о) исходный регистр n) следующая строка n
Введите строку (пустая строка - выход из программы) :
Программа завершена.
Обратите внимание, что функции ToUpper(), ToLower(), Transpose() и Dummy() имеют тот же самый тип, поэтому все они могут быть присвоены указателю pfun. В этой программе в качестве аргумента для show() применяется pfun, но можно также указывать непосредственно любое из имен четырех функций, как в show (Transpose, copy).
Читать дальше