Операция typedef
Любому типу данных, как стандартному, так и определенному пользователем, можно задать новое имя с помощью операции typedef: typedef <тип> <новое_имя>; Введенный таким образом новый тип используется аналогично стандартным типам, например, введя пользовательские типы: typedef unsigned int UINT; typedef char M_s[100]; декларации идентификаторов введенных типов имеют вид: UINT i, j; ® две переменные типа unsigned int M_s str[10]; ® массив из 10 строк по 100 символов
15.4. Указатели на функции В языке С идентификатор функции является константным указателем на начало функции в оперативной памяти и не может быть значением переменной. Но имеется возможность декларировать указатели на функции, с которыми можно обращаться как с переменными (например, можно создать массив, элементами которого будут указатели на функции). Рассмотрим методику работы с указателями на функции. 1. Как и любой объект языка С, указатель на функции необходимо декларировать. Формат объявления указателя на функции следующий: тип (*переменная-указатель)(список параметров); т.е. декларируется указатель, который можно устанавливать на функции, возвращающие результат указанного типа и которые имеют указанный список параметров. Наличие первых круглых скобок обязательно, так как без них – это декларация функции, которая возвращает указатель на результат своей работы указанного типа и имеет указанный список параметров. Например, объявление вида: float (* p_f)(char, float);говорит о том, что декларируется указатель p_f, который можно устанавливать на функции, возвращающие вещественный результат и имеющие два параметра: первый – символьного типа, а второй – вещественного типа. 2. Идентификатор функции является константным указателем, поэтому для того, чтобы установить переменную-указатель на конкретную функцию, достаточно ей присвоить ее идентификатор: переменная-указатель = ID _функции; Например, имеется функция с прототипом: float f1(char, float);тогда операция p_f = f1; установит указатель p_1 на данную функцию. 3. Вызов функции после установки на нее указателя выглядит так: (*переменная-указатель)(список аргументов); или переменная-указатель (список аргументов); После таких действий кроме стандартного обращения к функции: ID _функции(список аргументов); появляется еще два способа вызова функции: (*переменная-указатель)(список аргументов); или переменная-указатель (список аргументов); Последнее справедливо, так как p_f также является адресом начала функции в оперативной памяти. Для нашего примера к функции f1 можно обратиться следующими способами: f1(‘z’, 1.5); // Обращение к функции по ID (* p_f)(‘z’, 1.5); // Обращение к функции по указателю p_f(‘z’, 1.5); // Обращение к функции по ID указателя 4. Пусть имеется вторая функция с прототипом: float f2(char, float); тогда переустановив указатель p_f на эту функцию: p_f = f2; имеем опять три способа ее вызова: f2(‘z’, 1.5); // по ID функции (* p_f)(‘z’, 1.5); // по указателю на функцию p_f(‘z’, 1.5); // по ID указателя на функцию Основное назначение указателей на функции – это обеспечение возможности передачи идентификаторов функций в качестве параметров в функцию, которая реализует некоторый вычислительный процесс, используя формальное имя вызываемой функции.
Пример: написать функцию вычисления суммы sum,обозначив слагаемое формальной функцией fun(x), а при вызове функции суммирования передавать через параметр реальное имя функции, в которой запрограммирован явный вид этого слагаемого. Например, пусть требуется вычислить две суммы: и . Поместим слагаемые этих сумм в пользовательские функции f1 и f2, соответственно.
При этом для более удобной работы воспользуемся операцией typedef,введем пользовательский тип данных: указатель на функции, который можно устанавливать на функции, возвращающие результат, указанного типа, и имеющие указанный список параметров: typedef тип_результата (* переменная-указатель)(параметры); Тогда в списке параметров функции суммирования достаточно указывать фактические ID функций данного типа. Текст программы для решения данной задачи может быть следующим: ... // Декларация пользовательского типа: указатель на функции, // возвращающие float результат и имеющие один float параметр typedef float (*p_f)(float); float sum(p_f fun, int, float); // Декларации прототипов функций float f1(float); float f2(float); void main(void) { float x, s1, s2; int n; puts(" Введите кол-во слагаемых n и значение x: "); scanf(“%d%f”, &n, %x); s1=sum(f1, 2*n, x); s2=sum(f2, n, x); printf("\n\t N = %d, X = %f", n, x); printf(“\n\t Сумма 1 = %f\n\t Сумма 2 = %f", s1, s2); } // Функция вычисления суммы, первый параметр которой – формальное имя // функции, введенного с помощью typedef типа float sum(p_f fun, int n, float x) { float s=0; for(int i=1; i<=n; i++) s+=fun(x); return s; } // Первое слагаемое float f1(float r) { return (r/5.); } // Второе слагаемое float f2(float r) { return (r/2.); }
В заключение рассмотрим оптимальную передачу в функции одномерных и двухмерных массивов. Передача в функцию одномерного массива: void main (void) { int vect [20]; … fun(vect); … } void fun(int v [ ]) { … }
Передача в функцию двухмерного массива: void main(void) { int mass [ 2 ][ 3 ]={{1,2,3}, {4,5,6}}; … fun (mas); … } void fun(int m [ ][3]) { … }
|